Initial version imported to CVS
authorSamba Release Account <samba-bugs@samba.org>
Sat, 4 May 1996 07:50:46 +0000 (07:50 +0000)
committerSamba Release Account <samba-bugs@samba.org>
Sat, 4 May 1996 07:50:46 +0000 (07:50 +0000)
110 files changed:
COPYING [new file with mode: 0644]
README [new file with mode: 0644]
docs/THANKS [new file with mode: 0644]
docs/announce [new file with mode: 0644]
docs/history [new file with mode: 0644]
docs/htmldocs/wfw_slip.htm [new file with mode: 0644]
docs/manpages/nmbd.8 [new file with mode: 0644]
docs/manpages/samba.7 [new file with mode: 0644]
docs/manpages/smb.conf.5 [new file with mode: 0644]
docs/manpages/smbclient.1 [new file with mode: 0644]
docs/manpages/smbd.8 [new file with mode: 0644]
docs/manpages/smbrun.1 [new file with mode: 0644]
docs/manpages/smbstatus.1 [new file with mode: 0644]
docs/manpages/smbtar.1 [new file with mode: 0644]
docs/manpages/testparm.1 [new file with mode: 0644]
docs/manpages/testprns.1 [new file with mode: 0644]
docs/samba.lsm [new file with mode: 0644]
docs/textdocs/BROWSING.txt [new file with mode: 0644]
docs/textdocs/BUGS.txt [new file with mode: 0644]
docs/textdocs/DIAGNOSIS.txt [new file with mode: 0644]
docs/textdocs/DNIX.txt [new file with mode: 0644]
docs/textdocs/DOMAIN.txt [new file with mode: 0644]
docs/textdocs/ENCRYPTION.txt [new file with mode: 0644]
docs/textdocs/HINTS.txt [new file with mode: 0644]
docs/textdocs/INSTALL.sambatar [new file with mode: 0644]
docs/textdocs/PROJECTS [new file with mode: 0644]
docs/textdocs/Passwords.txt [new file with mode: 0644]
docs/textdocs/README.DCEDFS [new file with mode: 0644]
docs/textdocs/README.jis [new file with mode: 0644]
docs/textdocs/README.sambatar [new file with mode: 0644]
docs/textdocs/SCO.txt [new file with mode: 0644]
docs/textdocs/SMBTAR.notes [new file with mode: 0644]
docs/textdocs/Speed.txt [new file with mode: 0644]
docs/textdocs/Support.txt [new file with mode: 0644]
docs/textdocs/UNIX-SMB.txt [new file with mode: 0644]
docs/textdocs/WinNT.txt [new file with mode: 0644]
examples/README [new file with mode: 0644]
examples/dce-dfs/README [new file with mode: 0644]
examples/dce-dfs/smb.conf [new file with mode: 0644]
examples/misc/extra_smbstatus [new file with mode: 0644]
examples/misc/wall.perl [new file with mode: 0644]
examples/printing/smbprint [new file with mode: 0755]
examples/printing/smbprint.sysv [new file with mode: 0644]
examples/simple/README [new file with mode: 0644]
examples/simple/smb.conf [new file with mode: 0644]
examples/tridge/README [new file with mode: 0644]
examples/tridge/smb.conf [new file with mode: 0644]
examples/tridge/smb.conf.WinNT [new file with mode: 0644]
examples/tridge/smb.conf.fjall [new file with mode: 0644]
examples/tridge/smb.conf.lapland [new file with mode: 0644]
examples/tridge/smb.conf.vittjokk [new file with mode: 0644]
source/change-log [new file with mode: 0644]
source/client/client.c [new file with mode: 0644]
source/client/clitar.c [new file with mode: 0644]
source/include/byteorder.h [new file with mode: 0644]
source/include/charset.h [new file with mode: 0644]
source/include/clitar.h [new file with mode: 0644]
source/include/includes.h [new file with mode: 0644]
source/include/kanji.h [new file with mode: 0644]
source/include/local.h [new file with mode: 0644]
source/include/nameserv.h [new file with mode: 0644]
source/include/smb.h [new file with mode: 0644]
source/include/trans2.h [new file with mode: 0644]
source/include/version.h [new file with mode: 0644]
source/include/vt_mode.h [new file with mode: 0644]
source/lib/access.c [new file with mode: 0644]
source/lib/charcnv.c [new file with mode: 0644]
source/lib/charset.c [new file with mode: 0644]
source/lib/fault.c [new file with mode: 0644]
source/lib/getsmbpass.c [new file with mode: 0644]
source/lib/kanji.c [new file with mode: 0644]
source/lib/md4.c [new file with mode: 0644]
source/lib/system.c [new file with mode: 0644]
source/lib/ufc.c [new file with mode: 0644]
source/lib/username.c [new file with mode: 0644]
source/lib/util.c [new file with mode: 0644]
source/libsmb/nmblib.c [new file with mode: 0644]
source/libsmb/smbencrypt.c [new file with mode: 0644]
source/locking/locking.c [new file with mode: 0644]
source/md4.h [new file with mode: 0644]
source/nameserv.c [new file with mode: 0644]
source/nmbsync.c [new file with mode: 0644]
source/param/loadparm.c [new file with mode: 0644]
source/param/params.c [new file with mode: 0644]
source/passdb/smbpass.c [new file with mode: 0644]
source/printing/pcap.c [new file with mode: 0644]
source/printing/printing.c [new file with mode: 0644]
source/script/addtosmbpass [new file with mode: 0644]
source/script/installbin.sh [new file with mode: 0755]
source/script/installman.sh [new file with mode: 0755]
source/script/mksmbpasswd.sh [new file with mode: 0755]
source/script/revert.sh [new file with mode: 0755]
source/script/smbtar [new file with mode: 0644]
source/script/updatesmbpasswd.sh [new file with mode: 0755]
source/smbd/chgpasswd.c [new file with mode: 0644]
source/smbd/dir.c [new file with mode: 0644]
source/smbd/ipc.c [new file with mode: 0644]
source/smbd/mangle.c [new file with mode: 0644]
source/smbd/message.c [new file with mode: 0644]
source/smbd/password.c [new file with mode: 0644]
source/smbd/reply.c [new file with mode: 0644]
source/smbd/server.c [new file with mode: 0644]
source/smbd/smbrun.c [new file with mode: 0644]
source/smbd/trans2.c [new file with mode: 0644]
source/smbd/vt_mode.c [new file with mode: 0644]
source/utils/nmblookup.c [new file with mode: 0644]
source/utils/smbpasswd.c [new file with mode: 0644]
source/utils/status.c [new file with mode: 0644]
source/utils/testparm.c [new file with mode: 0644]
source/utils/testprns.c [new file with mode: 0644]

diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..a43ea21
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+       Appendix: How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..b7ef5d5
--- /dev/null
+++ b/README
@@ -0,0 +1,111 @@
+This is version 1.9 of Samba, the free SMB client and server for unix.
+
+>>>> Please read THE WHOLE of this file as it gives important information
+>>>> about the configuration and use of Samba.
+
+This software is freely distributable under the GNU public license, a
+copy of which you should have received with this software (in a file
+called COPYING). 
+
+WHAT CAN SAMBA DO?
+==================
+
+Here is a very short list of what samba includes, and what it does
+
+- a SMB server, to provide LanManager style file and print services to PCs
+
+- a Netbios (rfc1001/1002) nameserver
+
+- a ftp-like SMB client so you can access PC resources (disks and
+printers) from unix
+
+- a tar extension to the client for backing up PCs
+
+Related packages include:
+
+- ksmbfs, a linux-only filesystem allowing you to mount remote SMB
+filesystems from PCs on your linux box
+
+- tcpdump-smb, a extension to tcpdump to allow you to investigate SMB
+networking problems over netbeui and tcp/ip
+
+
+CONTRIBUTIONS
+=============
+
+If you want to contribute to the development of the software then
+please join the mailing list. I accept patches (preferably in 
+"diff -u" format) and am always glad to receive feedback or suggestions.
+
+You could also send hardware/software/money/jewelry or pizza
+vouchers directly to me. The pizza vouchers would be especially
+welcome :-)
+
+If you like a particular feature then look through the change-log and
+see who added it, then send them an email.
+
+Remember that free software of this kind lives or dies by the response
+we get. If noone tells us they like it then we'll probably move onto
+something else.
+
+Andrew Tridgell
+Email: samba-bugs@anu.edu.au
+
+3 Ballow Crescent
+Macgregor, A.C.T.
+2615 Australia
+
+
+MORE INFO
+=========
+
+DOCUMENTATION
+-------------
+
+There is quite a bit of documentation included with the package,
+including man pages, and lots of .txt files with hints and useful
+info.
+
+FTP SITE
+--------
+
+The main anonymous ftp distribution site for this software is
+nimbus.anu.edu.au in the directory pub/tridge/samba/.
+
+MAILING LIST
+------------
+
+There is a mailing list for discussion of Samba. To subscribe send
+mail to listproc@anu.edu.au with a body of "subscribe samba Your Name"
+
+To send mail to everyone on the list mail to samba@listproc.anu.edu.au
+
+There is also an announcement mailing list where I announce new
+versions.  To subscribe send mail to listproc@anu.edu.au with a body
+of "subscribe samba-announce Your Name". All announcements also go to
+the samba list.
+
+
+NEWS GROUP
+----------
+
+You might also like to look at the usenet news group
+comp.protocols.smb as it often contains lots of useful info and is
+frequented by lots of Samba users. The newsgroup was initially setup
+by people on the Samba mailing list. It is not, however, exclusive to
+Samba, it is a forum for discussing the SMB protocol (which Samba
+implements).
+
+
+WEB SITE
+--------
+
+A Samba WWW site has been setup with lots of useful info. Connect to:
+
+http://lake.canberra.edu.au/pub/samba/
+
+It is maintained by Paul Blackman (thanks Paul!). You can contact him
+at ictinus@lake.canberra.edu.au.
+
+
+
diff --git a/docs/THANKS b/docs/THANKS
new file mode 100644 (file)
index 0000000..6405da3
--- /dev/null
@@ -0,0 +1,119 @@
+=====================================================================
+This file is for thanks to individuals or organisations who have
+helped with the development of Samba, other than by coding or bug
+reports. Their contributions are gratefully acknowledged.
+
+Please refer to the manual pages and change-log for a list of those
+who have contributed in the form of patches, bug fixes or other 
+direct changes to the package.
+
+Contributions of any kind are welcomed. If you want to help then
+please contact Andrew.Tridgell@anu.edu.au, or via normal mail at 
+
+       Andrew Tridgell
+       3 Ballow Crescent
+       Macgregor, A.C.T
+       2615 Australia
+=====================================================================
+
+
+Lee Fisher (leefi@microsoft.com)
+Charles Fox (cfox@microsoft.com)
+Dan Perry (danp@exchnge.microsoft.com)
+
+    These Microsoft people have been very helpful and supportive of
+    the development of Samba. 
+
+    Lee very kindly supplied me with a copy of the X/Open SMB
+    specs. These have been invaluable in getting the details of the
+    implementation right. They will become even more important as we move
+    towards a Lanman 2.1 compliant server. Lee has provided very
+    useful advice on several aspects of the server.
+    Lee has also provided me with copies of Windows NTAS 3.1, Visual C
+    and a developers CD-ROM. Being able to run NT at home is a
+    great help.
+
+    Charles has helped out in numerous ways with the provision of SMB
+    specifications and helpful advice. He has been following the
+    discussion of Samba on the mailing list and has stepped in
+    regularly to clarify points and to offer help.
+    
+    Dan has put me in touch with NT developers to help sort out bugs and
+    compatability issues. He has also supplied me with a copy of the
+    NT browsing spec, which will help a lot in the development of the
+    Samba browser code.
+
+
+Bruce Perens (bruce@pixar.com)
+
+    In appreciation of his effort on Samba we have sent Andrew copies of
+    various Pixar computer-graphics software products. Pixar is best known
+    for its "Renderman" product, the 3-D renderer used by ILM to make special
+    effects for "Terminator II" and "Jurassic Park". We won the first Oscar
+    given to a computer graphic animated feature for our short film "Tin Toy".
+    Our retail products "Typestry" and "Showplace", incorporate the same
+    renderer used on the films, and are available on Windows and the
+    Macintosh.
+
+
+
+Henry Lee (hyl@microplex.co)
+
+    Henry sent me a M202 ethernet print server, making my little lan
+    one of the few home networks to have it's own print server!
+
+  ``Microplex Systems Ltd. is a manufacturer of local and wide area
+    network communications equipment based in beautiful Vancouver, British
+    Columbia, Canada.  Microplex's first products were synchronous wide
+    area network devices used in the mainframe communication networks.  In
+    August 1991 Microplex introduced its first LAN product, the M200 print
+    server, the first high performance print server under US$1,000.''
+
+
+Tom Haapanen (tomh@metrics.com)
+
+    Tom sent me two 16 bit SMC ethernet cards to replace my ancient 8
+    bit ones. The performance is much better!
+
+    Software Metrics Inc. is a small custom software development and
+    consulting firm located in Waterloo, Ontario, Canada.  We work
+    with a variety of environments (such as Windows, Windows NT and
+    Unix), tools and application areas, and can provide assistance for
+    development work ranging from a few days to to multiple man-year
+    projects.  You can find more information at http://www.metrics.com/.
+    
+
+Steve Kennedy (steve@gbnet.net)
+
+       Steve sent me 16Mb of ram so that I could install/test
+       NT3.5. I previous had only 8Mb ram in my test machine, which
+       wasn't enough to install a properly functioning copy of
+       NTAS. Being able to directly test NT3.5 allowed me to solve
+       several long standing NT<->Samba problems. Thanks Steve!
+
+John Terpstra (jht@aquasoft.com.au)
+
+        Aquasoft are a speciaist consulting company whose Samba using
+        customers span the world.
+
+        Aquasoft have been avid supporters of the Samba project. As a
+        token of appreciation Aquasoft have donated a 486DX2/66 PC with
+        a 540MB EIDE drive and 20MB RAM.
+
+        John has helped to isolate quite a few little glitches over time
+        and has managed to implement some very interesting installations
+        of Samba.
+
+        The donation of the new PC will make it possible to more fully
+        diagnose and observe the behaviour of Samba in conjuction with
+        other SMB protocol utilising systems.
+
+
+Timothy F. Sipples (tsipple@vnet.IBM.COM)
+Steve Withers (swithers@vnet.IBM.COM)
+
+       Tim and Steve from IBM organised a copy of the OS/2 developers
+       connection CD set for me, and gave lots of help in getting
+       OS/2 Warp installed. I hope this will allow me to finally fix
+       up those annoying OS/2 related Samba bugs that I have been
+       receiving reports of.
diff --git a/docs/announce b/docs/announce
new file mode 100644 (file)
index 0000000..f761320
--- /dev/null
@@ -0,0 +1,129 @@
+               Announcing Samba version 1.9
+               ============================
+
+What is Samba?
+--------------
+
+Samba is a Unix based SMB file server. This allows a Unix host to
+act as a file and print server for SMB clients. This includes
+Lan-Manager compatible clients such as LanManager for DOS, Windows for
+Workgroups, Windows NT, Windows 95, OS/2, Pathworks and many more.
+
+The package also includes a Unix SMB client and a netbios nameserver.
+
+What can it do for me?
+----------------------
+
+If you have any PCs running SMB clients, such as a PC running Windows
+for Workgroups, then you can mount file space or printers from a unix
+host, so that directories, files and printers on the unix host are
+available on the PC.
+
+The client part of the package will also allow you to attach to other
+SMB-based servers (such as windows NT and windows for workgroups) so
+that you can copy files to and from your unix host. The client also
+allows you to access a SMB printer (such as one attached to an OS/2 or
+WfWg server) from Unix, using an entry in /etc/printcap, or by
+explicitly specifying the command used to print files.
+
+What are it's features?
+------------------------
+
+Samba supports many features that are not supported in other SMB
+implementations (all of which are commercial). Some of it's features
+include host as well as username/password security, a unix client,
+automatic home directory exporting, automatic printer exporting, dead
+connection timeouts, umask support, guest connections, name mangling
+and hidden and system attribute mapping. Look at the man pages
+included with the package for a full list of features.
+
+What's new since 1.8?
+---------------------
+
+Lots of stuff. See the change log and man pages for details.
+
+Where can I get a client for my PC?
+-----------------------------------
+
+There is a free client for MS-DOS based PCs available from
+ftp.microsoft.com in the directory bussys/Clients/MSCLIENT/. Please
+read the licencing information before downloading. The built in
+Windows for Workgroups client is also very good.
+
+What network protocols are supported?
+-------------------------------------
+
+Currently only TCP/IP is supported. There has been some discussion
+about ports to other protocols but nothing is yet available.
+
+There is a free TCP/IP implementation for Windows for Workgroups
+available from ftp.microsoft.com (it's small, fast and quite reliable). 
+
+How much does it cost?
+----------------------
+
+Samba software is free software. It is available under the
+GNU Public licence in source code form at no cost. Please read the
+file COPYING that comes with the package for more information.
+
+What flavours of unix does it support?
+---------------------------------------
+
+The code has been written to be as portable as possible. It has been
+"ported" to many unixes, which mostly required changing only a few
+lines of code. It has been run (to my knowledge) on at least these
+unixes:
+
+Linux, SunOS, Solaris, SVR4, Ultrix, OSF1, AIX, BSDI, NetBSD,
+Sequent, HP-UX, SGI, FreeBSD, NeXT, ISC, A/UX, SCO, Intergraph,
+Domain/OS and DGUX.
+
+Some of these have received more testing than others. If it doesn't
+work with your unix then it should be easy to fix.
+
+Who wrote it?
+-------------
+
+Many people on the internet have contributed to the development of
+Samba. The maintainer and original author is Andrew Tridgell, but
+large parts of the package were contributed by several people from all
+over the world. Please look at the file `change-log' for information
+on who did what bits.
+
+Where can I get it?
+-------------------
+
+The package is available via anonymous ftp from nimbus.anu.edu.au in
+the directory pub/tridge/samba/. 
+
+What about SMBServer?
+---------------------
+
+Samba used to be known as SMBServer, until it was pointed out that
+Syntax, who make a commercial Unix SMB based server, have trademarked
+that name. The name was then changed to Samba. Also, in 1992 a very
+early incarnation of Samba was distributed as nbserver.
+
+If you see any copies of nbserver or smbserver on ftp sites please let
+me or the ftp archive maintainer know, as I want to get them deleted.
+
+Where can I get more info?
+---------------------------
+
+Please join the mailing list if you want to discuss the development or
+use of Samba. To join the mailing list send mail to
+listproc@listproc.anu.edu.au with a body of "subscribe samba Your
+Name".
+
+There is also an announcement mailing list for new version
+announcements. Subscribe as above but with "subscribe samba-announce
+Your Name".
+
+There is also often quite a bit of discussion about Samba on the
+newsgroup comp.protocols.smb.
+
+A WWW site with lots of Samba info can be found at 
+http://lake.canberra.edu.au/pub/samba/
+
+Andrew Tridgell (Contact: samba-bugs@anu.edu.au)
+January 1995
diff --git a/docs/history b/docs/history
new file mode 100644 (file)
index 0000000..83761e2
--- /dev/null
@@ -0,0 +1,165 @@
+Note: This file is now quite out of date - but perhaps that's
+appropriate?
+
+
+=========
+
+This is a short history of this project. It's not supposed to be
+comprehensive, just enough so that new users can get a feel for where
+this project has come from and maybe where it's going to.
+
+The whole thing really started in December 1991. I was (and still am)
+a PhD student in the Computer Sciences Laboratory at the Australian
+Netional University, in Canberra, Australia. We had just got a
+beta copy of eXcursion from Digital, and I was testing it on my PC. At
+this stage I was a MS-DOS user, dabbling in windows.
+
+eXcursion ran (at the time) only with Dec's `Pathworks' network for
+DOS. I had up till then been using PC-NFS to connect to our local sun
+workstations, and was reasonably happy with it. In order to run
+pathworks I had to stop using PC-NFS and try using pathworks to mount
+disk space. Unfortunately pathworks was only available for digital
+workstations running VMS or Ultrix so I couldn't mount from the suns
+anymore.
+
+I had access to a a decstation 3100 running Ultrix that I used to
+administer, and I got the crazy notion that the protocol that
+pathworks used to talk to ultrix couldn't be that hard, and maybe I
+could work it out. I had never written a network program before, and
+certainly didn't know what a socket was.
+
+In a few days, after looking at some example code for sockets, I
+discovered it was pretty easy to write a program to "spy" on the file
+sharing protocol. I wrote and installed this program (the sockspy.c
+program supplied with this package) and captured everything that the
+pathworks client said to the pathworks server.
+
+I then tried writing short C programs (using Turbo C under DOS) to do
+simple file operations on the network drive (open, read, cd etc) and
+looked at the packets that the server and client exchanged. From this
+I worked out what some of the bytes in the packets meant, and started
+to write my own program to do the same thing on a sun.
+
+After a day or so more I had my first successes and actually managed
+to get a connection and to read a file. From there it was all
+downhill, and a week later I was happily (if a little unreliably)
+mounting disk space from a sun to my PC running pathworks. The server
+code had a lot of `magic' values in it, which seemed to be always
+present with the ultrix server. It was not till 2 years later that I
+found out what all these values meant.
+
+Anyway, I thought other people might be interested in what I had done,
+so I asked a few people at uni, and noone seemed much interested. I
+also spoke to a person at Digital in Canberra (the person who had
+organised a beta test of eXcursion) and asked if I could distribute
+what I'd done, or was it illegal. It was then that I first heard the
+word "netbios" when he told me that he thought it was all covered by a
+spec of some sort (the netbios spec) and thus what I'd done was not
+only legal, but silly.
+
+I found the netbios spec after asking around a bit (the RFC1001 and
+RFC1002 specs) and found they looked nothing like what I'd written, so
+I thought maybe the Digital person was mistaken. I didn't realise RFCs
+referred to the name negotiation and packet encapsulation over TCP/IP,
+and what I'd written was really a SMB implementation.
+
+Anyway, he encouraged me to release it so I put out "Server 0.1" in
+January 1992. I got quite a good response from people wanting to use
+pathworks with non-digital unix workstations, and I soon fixed a few
+bugs, and released "Server 0.5" closely followed by "Server 1.0". All
+three releases came out within about a month of each other.
+
+At this point I got an X Terminal on my desk, and I no longer needed eXcursion
+and I prompty forgot about the whole project, apart from a few people
+who e-mailed me occasionally about it.
+
+Nearly two years then passed with just occasional e-mails asking about
+new versions and bugs. I even added a note to the ftp site asking for
+a volunteer to take over the code as I no longer used it. No one
+volunteered.
+
+During this time I did hear from a couple of people who said it should
+be possible to use my code with Lanmanager, but I never got any
+definite confirmation.
+
+One e-mail I got about the code did, however, make an impression. It
+was from Dan Shearer at the university of South Australia, and he said
+this:
+
+       I heard a hint about a free Pathworks server for Unix in the
+       Net channel of the Linux list. After quite a bit of chasing
+       (and lots of interested followups from other Linux people) I
+       got hold of a release news article from you, posted in Jan 92,
+       from someone in the UK.
+
+       Can you tell me what the latest status is? I think you might
+       suddenly find a whole lot of interested hackers in the Linux
+       world at least, which is a place where things tend to happen
+       fast (and even some reliable code gets written, BION!)
+
+I asked him what Linux was, and he told me it was a free Unix for PCs.
+This was in November 1992 and a few months later I was a Linux
+convert! I still didn't need a pathworks server though, so I didn't do
+the port, but I think Dan did.
+
+At about this time I got an e-mail from Digital, from a person working
+on the Alpha software distribution. He asked if I would mind if they
+included my server with the "contributed" cd-rom. This was a bit of a
+shock to me as I never expected Dec to ask me if they could use my
+code! I wrote back saying it was OK, but never heard from him again. I
+don't know if it went on the cd-rom.
+
+Anyway, the next big event was in December 1993, when Dan again sent
+me an e-mail saying my server had "raised it's ugly head" on
+comp.protocols.tcpip.ibmpc. I had a quick look on the group, and was
+surprised to see that there were people interested in this thing.
+
+At this time a person from our computer center offered me a couple of
+cheap ethernet cards (3c505s for $15 each) and coincidentially someone
+announced on one of the Linux channels that he had written a 3c505
+driver for Linux. I bought the cards, hacked the driver a little and
+setup a home network between my wifes PC and my Linux box. I then
+needed some way to connect the two, and I didn't own PC-NFS at home,
+so I thought maybe my server could be useful. On the newsgroup among
+the discussions of my server someone had mentioned that there was a
+free client that might work with my server that Microsoft had put up
+for ftp. I downloaded it and found to my surprise that it worked first
+time with my `pathworks' server!
+
+Well, I then did a bit of hacking, asked around a bit and found (I
+think from Dan) that the spec I needed was for the "SMB" protocol, and
+that it was available via ftp. I grabbed it and started removing all
+those ugly constants from the code, now that all was explained.
+
+On December 1st 1993 I announced the start of the "Netbios for Unix"
+project, seeding the mailing list with all the people who had e-mailed
+me over the years asking about the server.
+
+About 35 versions (and two months) later I wrote a short history of
+the project, which you have just read. There are now over a hundred
+people on the mailing list, and lots of people report that they use
+the code and like it. In a few days I will be announcing the release
+of version 1.6 to some of the more popular (and relevant) newsgroups.
+
+
+Andrew Tridgell
+6th February 1994
+
+---------------------
+
+It is now May 1995 and there are about 1400 people on the mailing
+list. I got downloads from the main Samba ftp site from around 5000
+unique hosts in a two month period. There are several mirror
+sites as well. The current version number is 1.9.13.
+
+---------------------
+
+
+---------------------
+It's now March 1996 and version 1.9.16alpha1 has just been
+released. There have been lots of changes recently with master browser
+support and the ability to do domain logons etc. Samba has also been
+ported to OS/2, the amiga and NetWare. There are now 3000 people on
+the samba mailing list.
+---------------------
diff --git a/docs/htmldocs/wfw_slip.htm b/docs/htmldocs/wfw_slip.htm
new file mode 100644 (file)
index 0000000..5b4a0a5
--- /dev/null
@@ -0,0 +1,175 @@
+<HTML>\r
+<HEAD>\r
+<TITLE>Peter Karrer Announces SLIP for WFW</TITLE>\r
+</HEAD>\r
+<BODY>\r
+<H1><I>Winserve</I></H1>\r
+<HR>\r
+<H2><I>Peter Karrer Announces SLIP for WFW</I></H2>\r
+[NEW 03-22-95)\r
+<HR>\r
+<B>Hello,</B>\r
+<P>\r
+I've discovered a way to run WfW's TCP/IP-32 over a SLIP packet driver. This\r
+allows WfW users to do Windows networking over dialup lines just like it is\r
+possible with NT and the Windows 95 beta!\r
+<P>\r
+For instance, you can mount Microsoft's FTP server as a network drive in File\r
+Manager or connect to an MS Mail post office over the Internet.  Of course,\r
+the usual Internet stuff works as well.  Another interesting site is\r
+WINSERVE.001; check out www.winserve.com.\r
+<HR>\r
+This method should work with any class 1 (Ethernet II) packet driver. However,\r
+I'm not in a position to try anything else than SLIPPER/CSLIPPER. \r
+<HR>\r
+<H3>Files you need:</H3>\r
+<B>WFWT32.EXE:</B>   ftp://ftp.microsoft.com/bussys/msclient/wfw/wfwt32.exe\r
+<P>\r
+  Microsoft's free TCP/IP for WfW.  It's a self-extracting archive which\r
+  should be executed in an empty directory.\r
+<P>\r
+<B>SLIPPER.EXE:</B>  ftp://biocserver.bioc.cwru.edu/pub/dos/slipper/slippr15.zip\r
+<P>\r
+  Peter Tattam's SLIP packet driver. CSLIPPER.EXE is a variant which supports\r
+  VJ header compression.\r
+<P>\r
+<B>PDETHER.EXE:</B>  ftp://sjf-lwp.idz.sjf.novell.com/odi/pdether/pde105.zip\r
+<P>\r
+  Don Provan's ODI-over-Packet Driver shim. This *must* be version 1.05 (or\r
+  above).\r
+<P>\r
+<B>LSL.COM:</B>\r
+<P>\r
+  Novell's LAN Support Layer.  If you're an owner of Windows 3.10, you'll\r
+  have it on one of your install disks.  Use "expand a:lsl.co_ lsl.com" to\r
+  expand it.  Microsoft has stopped bundling LSL.COM with WfW 3.11, though.\r
+  The newest version of LSL.COM can be downloaded as part of\r
+    ftp://ftp.novell.com/pub/netware/nwos/dosclnt12/vlms/vlmup2.exe.\r
+  However, it's not clear if this one may be legally used outside Netware\r
+  environments.\r
+<P>\r
+<B>NET.CFG:</B>\r
+<P>\r
+  A configuration file for LSL and PDETHER. It should contain the following\r
+  text:\r
+<P>\r
+<PRE>\r
+Link Support\r
+        Buffers 8 1600\r
+Link Driver PDETHER\r
+        Int 60\r
+        Frame Ethernet_II\r
+        Protocol IP   800  Ethernet_II\r
+        Protocol ARP  806  Ethernet_II\r
+        Protocol RARP 8035 Ethernet_II\r
+</PRE> \r
+<P>\r
+<B>DISCOMX.COM:</B>\r
+<P>\r
+  A little hack of mine to disable the COM port used by the SLIP packet driver.\r
+  Usage is e.g. "discomx 2" to disable COM2.  This should be run before\r
+  starting WfW, otherwise you'll get "device conflict" messages. Here it is:\r
+<P><PRE>\r
+begin 644 discomx.com\r
+F,=N)V8H.@`"P(+^!`/.N3XH="=MT!DN`XP/1XS')!R:)CP`$S2``\r
+`\r
+end\r
+ </PRE>\r
+  (Save this text to disk as <I>filename</I>, then run "uudecode <I>filename</I>".\r
+   uudecode can be found, for instance, at\r
+     ftp://ftp.switch.ch/mirror/simtel/msdos/starter/uudecode.com )\r
+<P>\r
+<B>LMHOSTS:</B>\r
+ <P> \r
+  An optional file which should be stored in your Windows subdirectory. It is\r
+  used to map NetBIOS computer names to IP addresses. Example:\r
+<P>\r
+<PRE>\r
+198.105.232.1   ftp             #PRE  # ftp.microsoft.com\r
+204.118.34.11   winserve.001    #PRE  # Winserve\r
+</PRE>\r
+<HR>\r
+<H3>How to install it:</H3>\r
+<P>\r
+<UL>\r
+<LI>Put the files mentioned above into a directory, e.g. C:\SLIP.\r
+<P>\r
+<LI>Put the following lines into AUTOEXEC.BAT:\r
+<P><PRE>\r
+  cd \slip\r
+  slipper com1 vec=60 baud=57600 ether (may vary with your modem setup)\r
+  lsl\r
+  pdether\r
+  discomx 1                            (must correspond to SLIPPER's COM port)\r
+</PRE>\r
+  (If you use another vec= setting, you must update that in NET.CFG as well.)\r
+  Use CSLIPPER instead of SLIPPER if your SLIP provider supports VJC.\r
+<P>\r
+<LI>Start WfW.\r
+<UL>\r
+<LI>Under Windows Setup, choose "Change Network Settings".\r
+<LI>Select  "Install Microsoft Windows Network".  \r
+<LI>In "Drivers...", choose "Add Adapter"\r
+  and install the "IPXODI Support driver (Ethernet) [ODI/NDIS3]".  \r
+<LI>In "Add Protocols...", select "Unlisted or Updated Protocol". When asked for a\r
+  driver disk, enter the directory where you expanded WFWT32.EXE. \r
+<LI>Configure TCP/IP (IP address, enable LMHOSTS lookup, try 204.118.34.11 as primary\r
+  WINS server). Remove all other protocols (NetBEUI, IPX/SPX).\r
+</UL>\r
+<P>\r
+<LI>Windows will probably update the first lines of AUTOEXEC.BAT with\r
+<P>\r
+<PRE>\r
+  c:\windows\net start\r
+  c:\windows\odihlp.exe.\r
+</PRE>\r
+  The "odihlp" line must be moved behind the "pdether" line.\r
+<P>\r
+<LI>Windows will also update NET.CFG with some "Frame" lines. These must\r
+  be removed (except "Frame Ethernet_II").\r
+<P>\r
+<LI>Somehow, you will have to dial in to your SLIP provider.  I do it manually\r
+  before slipper (or cslipper) gets loaded, using a DOS-based terminal program.\r
+  But there are some automatic dialers around.  I've seen recommendations for\r
+    ftp://mvmpc9.ciw.uni-karlsruhe.de/x-slip/slip_it.exe. \r
+<P>\r
+<LI>To connect to Microsoft's FTP server (or Winserve) go into File Manager,\r
+  choose "Connect Network drive" and enter "\\ftp" or "\\winserve.001" into\r
+  the "Path:" field.\r
+</UL>\r
+<HR>\r
+<H3>How it works:</H3>\r
+<P>\r
+Microsoft's TCP/IP-32 requires an NDIS3 interface. NDIS is Microsoft's way\r
+to interface with a network.\r
+<P>\r
+WfW also contains an NDIS3-over-ODI "shim", whose real mode component is\r
+ODIHLP.EXE.  ODI is Novell's way to interface with a network.\r
+<P>\r
+SLIPPER is a Packet Driver (PD) for use over serial lines.  PDs are everybody\r
+else's way to interface with a network.  SLIPPER's "ether" option makes it\r
+look like an Ethernet PD to applications using it. \r
+<P>\r
+A "shim" is a program which simulates a network application programming\r
+interface on top of another.\r
+<P>\r
+There is no NDIS SLIP driver which would work with WfW.\r
+<P>\r
+There is no NDIS-over-PD shim.\r
+<P>\r
+However, there's an ODI-over-PD shim (PDETHER) and an NDIS-over-ODI shim\r
+(ODIHLP etc.)\r
+<P>\r
+OK, so let's do NDIS-over-ODI-over-PD!\r
+   <P>\r
+This should have worked all the time; however, a non-feature in PDETHER\r
+versions < 1.05 has prevented the method from functioning until now.\r
+<HR>\r
+<B>Questions, suggestions etc. please to\r
+<P>\r
+<PRE>\r
+Peter Karrer           pkarrer@ife.ee.ethz.ch\r
+</PRE>\r
+</B>\r
+</BODY>\r
+</HTML>\r
diff --git a/docs/manpages/nmbd.8 b/docs/manpages/nmbd.8
new file mode 100644 (file)
index 0000000..e42f194
--- /dev/null
@@ -0,0 +1,491 @@
+.TH NMBD 8 17/1/1995 nmbd nmbd
+.SH NAME
+nmbd \- provide netbios nameserver support to clients
+.SH SYNOPSIS
+.B nmbd
+[
+.B -B
+.I broadcast address
+] [
+.B -I
+.I IP address
+] [
+.B -D
+] [
+.B -C comment string
+] [
+.B -G
+.I group name
+] [
+.B -H
+.I netbios hosts file
+] [
+.B -N
+.I netmask
+] [
+.B -d
+.I debuglevel
+] [
+.B -l
+.I log basename
+] [
+.B -n
+.I netbios name
+] [
+.B -p
+.I port number
+] [
+.B -s
+.I config file name
+]
+
+.SH DESCRIPTION
+This program is part of the Samba suite.
+
+.B nmbd
+is a server that understands and can reply to netbios
+name service requests, like those produced by LanManager
+clients. It also controls browsing.
+
+LanManager clients, when they start up, may wish to locate a LanManager server.
+That is, they wish to know what IP number a specified host is using.
+
+This program simply listens for such requests, and if its own name is specified
+it will respond with the IP number of the host it is running on. "Its own name"
+is by default the name of the host it is running on, but this can be overriden
+with the
+.B -n
+option (see "OPTIONS" below). Using the
+.B -S
+option (see "OPTIONS" below), it can also be instructed to respond with IP 
+information about other hosts, provided they are locatable via the 
+gethostbyname() call, or they are in a netbios hosts file.
+
+Nmbd can also be used as a WINS (Windows Internet Name Server)
+server. It will do this automatically by default. What this basically
+means is that it will respond to all name requests that it receives
+that are not broadcasts, as long as it can resolve the name.
+.SH OPTIONS
+.B -B
+
+.RS 3
+On some systems, the server is unable to determine the broadcast address to
+use for name registration requests. If your system has this difficulty, this 
+parameter may be used to specify an appropriate broadcast address. The 
+address should be given in standard "a.b.c.d" notation.
+
+Only use this parameter if you are sure that the server cannot properly 
+determine the proper broadcast address.
+
+The default broadcast address is determined by the server at run time. If it
+encounters difficulty doing so, it makes a guess based on the local IP
+number.
+.RE
+.B -I
+
+.RS 3
+On some systems, the server is unable to determine the correct IP
+address to use. This allows you to override the default choice.
+.RE
+
+.B -D
+
+.RS 3
+If specified, this parameter causes the server to operate as a daemon. That is,
+it detaches itself and runs in the background, fielding requests on the 
+appropriate port.
+
+By default, the server will NOT operate as a daemon.
+.RE
+
+.B -C comment string
+
+.RS 3
+This allows you to set the "comment string" that is shown next to the
+machine name in browse listings. 
+
+A %v will be replaced with the Samba version number.
+
+A %h will be replaced with the hostname.
+
+It defaults to "Samba %v".
+.RE
+
+.B -G
+
+.RS 3
+This option allows you to specify a netbios group (also known as
+lanmanager domain) that the server should be part of. You may include
+several of these on the command line if you like. Alternatively you
+can use the -H option to load a netbios hosts file containing domain names.
+
+At startup, unless the -R switch has been used, the server will
+attempt to register all group names in the hosts file and on the
+command line (from the -G option).
+
+The server will also respond to queries on this name.
+.RE
+
+.B -H
+
+.RS 3
+It may be useful in some situations to be able to specify a list of
+netbios names for which the server should send a reply if
+queried. This option allows that. The syntax is similar to the
+standard /etc/hosts file format, but has some extensions.
+
+The file contains three columns. Lines beginning with a # are ignored
+as comments. The first column is an IP address, or a hostname. If it
+is a hostname then it is interpreted as the IP address returned by
+gethostbyname() when read. Any IP address of 0.0.0.0 will be
+interpreted as the servers own IP address.
+
+The second column is a netbios name. This is the name that the server
+will respond to. It must be less than 20 characters long.
+
+The third column is optional, and is intended for flags. Currently the
+only flags supported are G, S and M. A G indicates that the name is a
+group (also known as domain) name.
+
+At startup all groups known to the server (either from this file or
+from the -G option) are registered on the network (unless the -R
+option has been selected).
+
+A S or G means that the specified address is a broadcast address of a
+network that you want people to be able to browse you from. Nmbd will
+search for a master browser in that domain and will send host
+announcements to that machine, informing it that the specifed somain
+is available.
+
+A M means that this name is the default netbios name for this
+machine. This has the same affect as specifying the -n option to nmbd.
+
+After startup the server waits for queries, and will answer queries to
+any name known to it. This includes all names in the netbios hosts
+file (if any), it's own name, and any names given with the -G option.
+
+The primary intention of the -H option is to allow a mapping from
+netbios names to internet domain names, and to allow the specification
+of groups that the server should be part of.
+
+.B Example:
+
+        # This is a sample netbios hosts file
+
+        # DO NOT USE THIS FILE AS-IS
+        # YOU MAY INCONVENIENCE THE OWNERS OF THESE IPs
+        # if you want to include a name with a space in it then 
+        # use double quotes.
+
+        # first put ourselves in the group LANGROUP
+        0.0.0.0 LANGROUP G
+
+        # next add a netbios alias for a faraway host
+        arvidsjaur.anu.edu.au ARVIDSJAUR
+
+        # finally put in an IP for a hard to find host
+        130.45.3.213 FREDDY
+
+        # now we want another subnet to be able to browse
+        # us in the workgroup UNIXSERV
+        192.0.2.255  UNIXSERV G
+
+.RE
+
+.B -M
+.I workgroup name
+
+.RS 3
+If this parameter is given, the server will look for a master browser
+for the specified workgroup name, report success or failure, then
+exit. If successful, the IP address of the name located will be
+reported. 
+
+If you use the workgroup name "-" then nmbd will search for a master
+browser for any workgroup by using the name __MSBROWSE__.
+
+This option is meant to be used interactively on the command line, not
+as a daemon or in inetd.
+
+.RE
+.B -N
+
+.RS 3
+On some systems, the server is unable to determine the netmask. If
+your system has this difficulty, this parameter may be used to specify
+an appropriate netmask. The mask should be given in standard
+"a.b.c.d" notation.
+
+Only use this parameter if you are sure that the server cannot properly 
+determine the proper netmask.
+
+The default netmask is determined by the server at run time. If it
+encounters difficulty doing so, it makes a guess based on the local IP
+number.
+.RE
+
+.B -d
+.I debuglevel
+.RS 3
+
+debuglevel is an integer from 0 to 5.
+
+The default value if this parameter is not specified is zero.
+
+The higher this value, the more detail will be logged to the log files about
+the activities of the server. At level 0, only critical errors and serious 
+warnings will be logged. Level 1 is a reasonable level for day to day running
+- it generates a small amount of information about operations carried out.
+
+Levels above 1 will generate considerable amounts of log data, and should 
+only be used when investigating a problem. Levels above 3 are designed for 
+use only by developers and generate HUGE amounts of log data, most of which 
+is extremely cryptic.
+.RE
+
+.B -l
+.I log file
+
+.RS 3
+If specified,
+.I logfile
+specifies a base filename into which operational data from the running server
+will be logged.
+
+The default base name is specified at compile time.
+
+The base name is used to generate actual log file names. For example, if the
+name specified was "log", the following files would be used for log data:
+
+.RS 3
+log.nmb (containing debugging information)
+
+log.nmb.in (containing inbound transaction data)
+
+log.nmb.out (containing outbound transaction data)
+.RE
+
+The log files generated are never removed by the server.
+.RE
+.RE
+
+.B -n
+.I netbios name
+
+.RS 3
+This parameter tells the server what netbios name to respond with when 
+queried. The same name is also registered on startup unless the -R 
+parameter was specified.
+
+The default netbios name used if this parameter is not specified is the 
+name of the host on which the server is running.
+.RE
+
+.B -p
+.I port number
+.RS 3
+
+port number is a positive integer value.
+
+The default value if this parameter is not specified is 137.
+
+This number is the port number that will be used when making connections to
+the server from client software. The standard (well-known) port number for the
+server is 137, hence the default. If you wish to run the server as an ordinary
+user rather than as root, most systems will require you to use a port number
+greater than 1024 - ask your system administrator for help if you are in this
+situation.
+
+Note that the name server uses UDP, not TCP!
+
+This parameter is not normally specified except in the above situation.
+.RE
+.SH FILES
+
+.B /etc/inetd.conf
+
+.RS 3
+If the server is to be run by the inetd meta-daemon, this file must contain
+suitable startup information for the meta-daemon. See the section 
+"INSTALLATION" below.
+.RE
+
+.B /etc/rc.d/rc.inet2
+
+.RS 3
+(or whatever initialisation script your system uses)
+
+If running the server as a daemon at startup, this file will need to contain
+an appropriate startup sequence for the server. See the section "Installation"
+below.
+.RE
+
+.B /etc/services
+
+.RS 3
+If running the server via the meta-daemon inetd, this file must contain a
+mapping of service name (eg., netbios-ns)  to service port (eg., 137) and
+protocol type (eg., udp). See the section "INSTALLATION" below.
+.RE
+.RE
+
+.SH ENVIRONMENT VARIABLES
+Not applicable.
+
+.SH INSTALLATION
+The location of the server and its support files is a matter for individual
+system administrators. The following are thus suggestions only.
+
+It is recommended that the server software be installed under the /usr/local
+hierarchy, in a directory readable by all, writeable only by root. The server
+program itself should be executable by all, as users may wish to run the 
+server themselves (in which case it will of course run with their privileges).
+The server should NOT be setuid or setgid!
+
+The server log files should be put in a directory readable and writable only
+by root, as the log files may contain sensitive information.
+
+The remaining notes will assume the following:
+
+.RS 3
+nmbd (the server program) installed in /usr/local/smb
+
+log files stored in /var/adm/smblogs
+.RE
+
+The server may be run either as a daemon by users or at startup, or it may
+be run from a meta-daemon such as inetd upon request. If run as a daemon, the
+server will always be ready, so starting sessions will be faster. If run from 
+a meta-daemon some memory will be saved and utilities such as the tcpd 
+TCP-wrapper may be used for extra security.
+
+When you've decided, continue with either "Running the server as a daemon" or
+"Running the server on request".
+.SH RUNNING THE SERVER AS A DAEMON
+To run the server as a daemon from the command line, simply put the "-D" option
+on the command line. There is no need to place an ampersand at the end of the
+command line - the "-D" option causes the server to detach itself from the
+tty anyway.
+
+Any user can run the server as a daemon (execute permissions permitting, of 
+course). This is useful for testing purposes.
+
+To ensure that the server is run as a daemon whenever the machine is started,
+you will need to modify the system startup files. Wherever appropriate (for
+example, in /etc/rc.d/rc.inet2), insert the following line, substituting 
+values appropriate to your system:
+
+.RS 3
+/usr/local/smb/nmbd -D -l/var/adm/smblogs/log
+.RE
+
+(The above should appear in your initialisation script as a single line. 
+Depending on your terminal characteristics, it may not appear that way in
+this man page. If the above appears as more than one line, please treat any 
+newlines or indentation as a single space or TAB character.)
+
+If the options used at compile time are appropriate for your system, all
+parameters except the desired debug level and "-D" may be omitted. See the
+section on "Options" above.
+.SH RUNNING THE SERVER ON REQUEST
+If your system uses a meta-daemon such as inetd, you can arrange to have the
+SMB name server started whenever a process attempts to connect to it. This 
+requires several changes to the startup files on the host machine. If you are
+experimenting as an ordinary user rather than as root, you will need the 
+assistance of your system administrator to modify the system files.
+
+First, ensure that a port is configured in the file /etc/services. The 
+well-known port 137 should be used if possible, though any port may be used.
+
+Ensure that a line similar to the following is in /etc/services:
+
+.RS 3
+netbios-ns     137/udp
+.RE
+
+Note for NIS/YP users: You may need to rebuild the NIS service maps rather
+than alter your local /etc/services file.
+
+Next, put a suitable line in the file /etc/inetd.conf (in the unlikely event
+that you are using a meta-daemon other than inetd, you are on your own). Note
+that the first item in this line matches the service name in /etc/services.
+Substitute appropriate values for your system in this line (see
+.B inetd(8)):
+
+.RS 3
+netbios-ns dgram udp wait root /usr/local/smb/nmbd -l/var/adm/smblogs/log
+.RE
+
+(The above should appear in /etc/inetd.conf as a single line. Depending on 
+your terminal characteristics, it may not appear that way in this man page.
+If the above appears as more than one line, please treat any newlines or 
+indentation as a single space or TAB character.)
+
+Note that there is no need to specify a port number here, even if you are 
+using a non-standard port number.
+.SH TESTING THE INSTALLATION
+If running the server as a daemon, execute it before proceeding. If
+using a meta-daemon, either restart the system or kill and restart the 
+meta-daemon. Some versions of inetd will reread their configuration tables if
+they receive a HUP signal.
+
+To test whether the name server is running, start up a client
+.I on a different machine
+and see whether the desired name is now present. Alternatively, run 
+the nameserver
+.I on a different machine
+specifying "-L netbiosname", where "netbiosname" is the name you have 
+configured the test server to respond with. The command should respond 
+with success, and the IP number of the machine using the specified netbios 
+name. You may need the -B parameter on some systems. See the README
+file for more information on testing nmbd.
+
+.SH VERSION
+This man page is (mostly) correct for version 1.9.00 of the Samba suite, plus some
+of the recent patches to it. These notes will necessarily lag behind 
+development of the software, so it is possible that your version of 
+the server has extensions or parameter semantics that differ from or are not 
+covered by this man page. Please notify these to the address below for 
+rectification.
+.SH SEE ALSO
+.B inetd(8),
+.B smbd(8), 
+.B smb.conf(5),
+.B smbclient(1),
+.B testparm(1), 
+.B testprns(1)
+
+.SH DIAGNOSTICS
+[This section under construction]
+
+Most diagnostics issued by the server are logged in the specified log file. The
+log file name is specified at compile time, but may be overridden on the
+command line.
+
+The number and nature of diagnostics available depends on the debug level used
+by the server. If you have problems, set the debug level to 3 and peruse the
+log files.
+
+Most messages are reasonably self-explanatory. Unfortunately, at time of
+creation of this man page the source code is still too fluid to warrant
+describing each and every diagnostic. At this stage your best bet is still
+to grep the source code and inspect the conditions that gave rise to the 
+diagnostics you are seeing.
+
+.SH BUGS
+None known.
+.SH CREDITS
+The original Samba software and related utilities were created by 
+Andrew Tridgell (samba-bugs@anu.edu.au). Andrew is also the Keeper
+of the Source for this project.
+
+This man page written by Karl Auer (Karl.Auer@anu.edu.au)
+
+See
+.B smb.conf(5) for a full list of contributors and details on how to 
+submit bug reports, comments etc.
+
+
+
+
+
diff --git a/docs/manpages/samba.7 b/docs/manpages/samba.7
new file mode 100644 (file)
index 0000000..0c81f73
--- /dev/null
@@ -0,0 +1,190 @@
+.TH SAMBA 7 29/3/95 Samba Samba
+.SH NAME
+Samba \- a LanManager like fileserver for Unix
+.SH SYNOPSIS
+.B Samba
+.SH DESCRIPTION
+The
+.B Samba
+software suite is a collection of programs that implements the SMB
+protocol for unix systems. This protocol is sometimes also referred to
+as the LanManager or Netbios protocol.
+
+.SH COMPONENTS
+
+The Samba suite is made up of several components. Each component is
+described in a separate manual page. It is strongly recommended that
+you read the documentation that comes with Samba and the manual pages
+of those components that you use. If the manual pages aren't clear
+enough then please send me a patch!
+
+The smbd(8) daemon provides the file and print services to SMB clents,
+such as Windows for Workgroups, Windows NT or LanManager. The
+configuration file for this daemon is described in smb.conf(5).
+
+The nmbd(8) daemon provides Netbios nameserving and browsing
+support. It can also be run interactively to query other name service
+daemons.
+
+The smbclient(1) program implements a simple ftp-like client. This is
+useful for accessing SMB shares on other compatible servers (such as
+WfWg), and can also be used to allow a unix box to print to a printer
+attached to any SMB server (such as a PC running WfWg).
+
+The testparm(1) utility allows you to test your smb.conf(5)
+configuration file.
+
+The smbstatus(1) utility allows you to tell who is currently using the
+smbd(8) server.
+
+.SH AVAILABILITY
+
+The Samba software suite is licensed under the Gnu Public License. A
+copy of that license should have come with the package. You are
+encouraged to distribute copies of the Samba suite, but please keep it
+intact.
+
+The latest version of the Samba suite can be obtained via anonymous
+ftp from nimbus.anu.edu.au in the directory pub/tridge/samba/. It is
+also available on several mirror sites worldwide.
+
+You may also find useful information about Samba on the newsgroup
+comp.protocols.smb and the Samba mailing list. Details on how to join
+the mailing list are given in the README file that comes with Samba.
+
+If you have access to a WWW viewer (such as Netscape or Mosaic) then
+you will also find lots of useful information, including back issues
+of the Samba mailing list, at http://lake.canberra.edu.au/pub/samba/
+
+.SH AUTHOR
+
+The main author of the Samba suite is Andrew Tridgell. He may be
+contacted via e-mail at samba-bugs@anu.edu.au.
+
+There have also been an enourmous number of contributors to Samba from
+all over the world. A partial list of these contributors is included
+in the CREDITS section below. The list is, however, badly out of
+date. More up to date info may be obtained from the change-log that
+comes with the Samba source code.
+
+.SH CONTRIBUTIONS
+
+If you wish to contribute to the Samba project, then I suggest you
+join the Samba mailing list.
+
+If you have patches to submit or bugs to report then you may mail them
+directly to samba-bugs@anu.edu.au. Note, however, that due to the
+enourmous popularity of this package I may take some time to repond to
+mail. I prefer patches in "diff -u" format.
+
+.SH CREDITS
+
+Contributors to the project are (in alphabetical order by email address):
+
+(NOTE: This list is very out of date)
+
+ Adams, Graham
+       (gadams@ddrive.demon.co.uk)
+ Allison, Jeremy
+       (jeremy@netcom.com)
+ Andrus, Ross
+       (ross@augie.insci.com)
+ Auer, Karl
+       (Karl.Auer@anu.edu.au)
+ Bogstad, Bill
+       (bogstad@cs.jhu.edu)
+ Boreham, Bryan
+       (Bryan@alex.com)
+ Boreham, David
+       (davidb@ndl.co.uk)
+ Butler, Michael
+       (imb@asstdc.scgt.oz.au)
+ ???
+       (charlie@edina.demon.co.uk)
+ Chua, Michael
+       (lpc@solomon.technet.sg)
+ Cochran, Marc
+       (mcochran@wellfleet.com)
+ Dey, Martin N
+       (mnd@netmgrs.co.uk)
+ Errath, Maximilian
+       (errath@balu.kfunigraz.ac.at)
+ Fisher, Lee
+       (leefi@microsoft.com)
+ Foderaro, Sean
+       (jkf@frisky.Franz.COM)
+ Greer, Brad
+       (brad@cac.washington.edu)
+ Griffith, Michael A
+       (grif@cs.ucr.edu)
+ Grosen, Mark
+       (MDGrosen@spectron.COM)
+ ????
+       (gunjkoa@dep.sa.gov.au)
+ Haapanen, Tom
+       (tomh@metrics.com)
+ Hench, Mike
+       (hench@cae.uwm.edu)
+ Horstman, Mark A
+       (mh2620@sarek.sbc.com)
+ Hudson, Tim
+       (tim.hudson@gslmail.mincom.oz.au)
+ Hulthen, Erik Magnus
+       (magnus@axiom.se)
+ ???
+       (imb@asstdc.scgt.oz.au)
+ Iversen, Per Steinar
+       (iversen@dsfys1.fi.uib.no)
+ Kaara, Pasi
+       (ppk@atk.tpo.fi)
+ Karman, Merik
+       (merik@blackadder.dsh.oz.au)
+ Kiff, Martin
+       (mgk@newton.npl.co.uk)
+ Kiick, Chris
+       (cjkiick@flinx.b11.ingr.com)
+ Kukulies, Christoph
+       (kuku@acds.physik.rwth-aachen.de)
+ ???
+       (lance@fox.com)
+ Lendecke, Volker
+       (lendecke@namu01.gwdg.de)
+ ???
+       (lonnie@itg.ti.com)
+ Mahoney, Paul Thomas
+       (ptm@xact1.xact.com)
+ Mauelshagen, Heinz
+       (mauelsha@ez.da.telekom.de)
+ Merrick, Barry G
+       (bgm@atml.co.uk)
+ Mol, Marcel
+       (marcel@fanout.et.tudeflt.nl)
+ ???
+       (njw@cpsg.com.au)
+ ???
+       (noses@oink.rhein.de)
+ Owens, John
+       (john@micros.com)
+ Pierson, Jacques
+       (pierson@ketje.enet.dec.com)
+ Powell, Mark
+       (mark@scot1.ucsalf.ac.uk)
+ Reiz, Steven
+       (sreiz@aie.nl)
+ Schlaeger, Joerg
+       (joergs@toppoint.de)
+ S{rkel{, Vesa
+       (vesku@rankki.kcl.fi)
+ Tridgell, Andrew
+       (samba-bugs@anu.edu.au)
+ Troyer, Dean
+       (troyer@saifr00.ateng.az.honeywell.com)
+ Wakelin, Ross
+       (rossw@march.co.uk)
+ Wessels, Stefan
+       (SWESSELS@dos-lan.cs.up.ac.za)
+ Young, Ian A
+       (iay@threel.co.uk)
+ van der Zwan, Paul
+       (paulzn@olivetti.nl)
+
diff --git a/docs/manpages/smb.conf.5 b/docs/manpages/smb.conf.5
new file mode 100644 (file)
index 0000000..933d71f
--- /dev/null
@@ -0,0 +1,2719 @@
+.TH SMB.CONF 5 11/10/94 smb.conf smb.conf
+.SH NAME
+smb.conf \- configuration file for smbd
+.SH SYNOPSIS
+.B smb.conf
+.SH DESCRIPTION
+The
+.B smb.conf
+file is a configuration file for the Samba suite.
+
+.B smb.conf
+contains runtime configuration information for the
+.B smbd
+program. The
+.B smbd
+program provides LanManager-like services to clients
+using the SMB protocol.
+
+.SH FILE FORMAT
+The file consists of sections and parameters. A section begins with the 
+name of the section in square brackets and continues until the next
+section begins. Sections contain parameters of the form 'name = value'.
+
+The file is line-based - that is, each newline-terminated line represents
+either a comment, a section name or a parameter.
+
+Section and parameter names are not case sensitive.
+
+Only the first equals sign in a parameter is significant. Whitespace before 
+or after the first equals sign is discarded. Leading, trailing and internal
+whitespace in section and parameter names is irrelevant. Leading and
+trailing whitespace in a parameter value is discarded. Internal whitespace
+within a parameter value is retained verbatim.
+
+Any line beginning with a semicolon is ignored, as are lines containing 
+only whitespace.
+
+Any line ending in a \\ is "continued" on the next line in the
+customary unix fashion.
+
+The values following the equals sign in parameters are all either a string
+(no quotes needed) or a boolean, which may be given as yes/no, 0/1 or
+true/false. Case is not significant in boolean values, but is preserved
+in string values. Some items such as create modes are numeric.
+.SH SERVICE DESCRIPTIONS
+Each section in the configuration file describes a service. The section name
+is the service name and the parameters within the section define the service's
+attributes.
+
+There are three special sections, [global], [homes] and [printers], which are
+described under 'special sections'. The following notes apply to ordinary 
+service descriptions.
+
+A service consists of a directory to which access is being given plus a 
+description of the access rights which are granted to the user of the 
+service. Some housekeeping options are also specifiable.
+
+Services are either filespace services (used by the client as an extension of
+their native file systems) or printable services (used by the client to access
+print services on the host running the server).
+
+Services may be guest services, in which case no password is required to
+access them. A specified guest account is used to define access privileges
+in this case.
+
+Services other than guest services will require a password to access
+them. The client provides the username. As many clients only provide
+passwords and not usernames, you may specify a list of usernames to
+check against the password using the "user=" option in the service
+definition. 
+
+Note that the access rights granted by the server are masked by the access
+rights granted to the specified or guest user by the host system. The 
+server does not grant more access than the host system grants.
+
+The following sample section defines a file space service. The user has write
+access to the path /home/bar. The service is accessed via the service name 
+"foo":
+
+       [foo]
+               path = /home/bar
+               writable = true
+
+The following sample section defines a printable service. The service is 
+readonly, but printable. That is, the only write access permitted is via 
+calls to open, write to and close a spool file. The 'guest ok' parameter 
+means access will be permitted as the default guest user (specified elsewhere):
+
+       [aprinter]
+               path = /usr/spool/public
+               read only = true
+               printable = true
+               public = true
+
+.SH SPECIAL SECTIONS
+
+.SS The [global] section
+.RS 3
+Parameters in this section apply to the server as a whole, or are defaults
+for services which do not specifically define certain items. See the notes
+under 'Parameters' for more information.
+.RE
+
+.SS The [homes] section
+.RS 3
+If a section called 'homes' is included in the configuration file, services
+connecting clients to their home directories can be created on the fly by the
+server.
+
+When the connection request is made, the existing services are scanned. If a
+match is found, it is used. If no match is found, the requested service name is
+treated as a user name and looked up in the local passwords file. If the
+name exists and the correct password has been given, a service is created
+by cloning the [homes] section.
+
+Some modifications are then made to the newly created section:
+
+.RS 3
+The service name is changed from 'homes' to the located username
+
+If no path was given, the path is set to the user's home directory.
+.RE
+
+If you decide to use a path= line in your [homes] section then you may
+find it useful to use the %S macro. For example path=/data/pchome/%S
+would be useful if you have different home directories for your PCs
+than for unix access.
+
+This is a fast and simple way to give a large number of clients access to
+their home directories with a minimum of fuss.
+
+A similar process occurs if the requested service name is "homes", except that
+the service name is not changed to that of the requesting user. This method
+of using the [homes] section works well if different users share a client PC.
+
+The [homes] section can specify all the parameters a normal service section
+can specify, though some make more sense than others. The following is a 
+typical and suitable [homes] section:
+
+       [homes]
+               writable = yes
+
+An important point:
+
+.RS 3
+If guest access is specified in the [homes] section, all home directories will
+be accessible to all clients
+.B without a password.
+In the very unlikely event
+that this is actually desirable, it would be wise to also specify read only
+access.
+.RE
+.RE
+
+Note that the browseable flag for auto home directories will be
+inherited from the global browseable flag, not the [homes] browseable
+flag. This is useful as it means setting browseable=no in the [homes]
+section will hide the [homes] service but make any auto home
+directories visible.
+
+.SS The [printers] section
+.RS 3
+This section works like [homes], but for printers.
+
+If a [printers] section occurs in the configuration file, users are able 
+to connect to any printer specified in the local host's printcap file.
+
+When a connection request is made, the existing services are scanned. If a
+match is found, it is used. If no match is found, but a [homes] section
+exists, it is used as described above. Otherwise, the requested service name is
+treated as a printer name and the appropriate printcap file is scanned to
+see if the requested service name is a valid printer name. If a match is
+found, a new service is created by cloning the [printers] section.
+
+A few modifications are then made to the newly created section:
+
+.RS 3
+The service name is set to the located printer name
+
+If no printer name was given, the printer name is set to the located printer
+name
+
+If the service does not permit guest access and no username was given, the 
+username is set to the located printer name.
+.RE
+
+Note that the [printers] service MUST be printable - if you specify otherwise,
+the server will refuse to load the configuration file.
+
+Typically the path specified would be that of a world-writable spool directory
+with the sticky bit set on it. A typical [printers] entry would look like this:
+
+       [printers]
+               path = /usr/spool/public
+               writable = no
+               public = yes
+               printable = yes 
+
+All aliases given for a printer in the printcap file are legitimate printer
+names as far as the server is concerned. If your printing subsystem doesn't
+work like that, you will have to set up a pseudo-printcap. This is a file
+consisting of one or more lines like this:
+
+        alias|alias|alias|alias...
+
+Each alias should be an acceptable printer name for your printing 
+subsystem. In the [global] section, specify the new file as your printcap.
+The server will then only recognise names found in your pseudo-printcap,
+which of course can contain whatever aliases you like. The same technique
+could be used simply to limit access to a subset of your local printers.
+
+An alias, by the way, is defined as any component of the first entry of a 
+printcap record. Records are separated by newlines, components (if there are 
+more than one) are separated by vertical bar symbols ("|").
+.SH PARAMETERS
+Parameters define the specific attributes of services.
+
+Some parameters are specific to the [global] section (eg., security).
+Some parameters are usable in all sections (eg., create mode). All others are
+permissible only in normal sections. For the purposes of the following
+descriptions the [homes] and [printers] sections will be considered normal.
+The letter 'G' in parentheses indicates that a parameter is specific to the
+[global] section. The letter 'S' indicates that a parameter can be
+specified in a secvice specific section. Note that all S parameters
+can also be specified in the [global] section - in which case they
+will define the default behaviour for all services.
+
+Parameters are arranged here in alphabetical order - this may not create
+best bedfellows, but at least you can find them! Where there are synonyms,
+the preferred synonym is described, others refer to the preferred synonym.
+
+.SS VARIABLE SUBSTITUTIONS
+
+Many of the strings that are settable in the config file can take
+substitutions. For example the option "path = /tmp/%u" would be
+interpreted as "path = /tmp/john" if the user connected with the
+username john.
+
+These substitutions are mostly noted in the descriptions below, but
+there are some general substitions which apply whenever they might be
+relevant. These are:
+
+%S = the name of the current service, if any
+
+%P = the root directory of the current service, if any
+
+%u = user name of the current service, if any
+
+%g = primary group name of %u
+
+%U = session user name (the user name that the client wanted, not
+necessarily the same as the one they got)
+
+%G = primary group name of %U
+
+%H = the home directory of the user given by %u
+
+%v = the Samba version
+
+%h = the hostname that Samba is running on
+
+%m = the netbios name of the client machine (very useful)
+
+%L = the netbios name of the server. This allows you to change your
+config based on what the client calls you. Your server can have a "dual
+personality".
+
+%M = the internet name of the client machine
+
+%d = The process id of the current server process
+
+%a = the architecture of the remote machine. Only some are recognised,
+and those may not be 100% reliable. It currently recognises Samba,
+WfWg, WinNT and Win95. Anything else will be known as "UNKNOWN". If it
+gets it wrong then sending me a level 3 log should allow me to fix it.
+
+%I = The IP address of the client machine
+
+%T = the current date and time
+
+There are some quite creative things that can be done with these
+substitutions and other smb.conf options.
+
+.SS NAME MANGLING
+
+Samba supports "name mangling" so that Dos and Windows clients can use
+files that don't conform to the 8.3 format. It can also be set to adjust
+the case of 8.3 format filenames.
+
+There are several options that control the way mangling is performed,
+and they are grouped here rather than listed separately. For the
+defaults look at the output of the testparm program.
+
+All of these options can be set separately for each service (or
+globally, of course).
+
+The options are:
+
+"mangle case = yes/no" controls if names that have characters that
+aren't of the "default" case are mangled. For example, if this is yes
+then a name like "Mail" would be mangled. Default no.
+
+"case sensitive = yes/no" controls whether filenames are case
+sensitive. If they aren't then Samba must do a filename search and
+match on passed names. Default no.
+
+"default case = upper/lower" controls what the default case is for new
+filenames. Default lower.
+
+"preserve case = yes/no" controls if new files are created with the
+case that the client passes, or if they are forced to be the "default"
+case. Default no.
+
+"short preserve case = yes/no" controls if new files which conform to 8.3
+syntax, that is all in upper case and of suitable length, are created
+upper case, or if they are forced to be the "default" case. This option can
+be use with "preserve case = yes" to permit long filenames to retain their
+case, while short names are lowered. Default no.
+
+.SS COMPLETE LIST OF GLOBAL PARAMETER
+
+Here is a list of all global parameters. See the section of each
+parameter for details.  Note that some are synonyms.
+
+auto services
+
+config file
+
+deadtime
+
+debuglevel
+
+default
+
+default service
+
+dfree command
+
+encrypt passwords
+
+getwd cache
+
+hosts equiv
+
+include
+
+keepalive
+
+lock dir
+
+load printers
+
+lock directory
+
+log file
+
+log level
+
+lpq cache time
+
+mangled stack
+
+max log size
+
+max packet
+
+max xmit
+
+message command
+
+null passwords
+
+os level
+
+packet size
+
+passwd chat
+
+passwd program
+
+password level
+
+password server
+
+preferred master
+
+preload
+
+printing
+
+printcap name
+
+protocol
+
+read bmpx
+
+read prediction
+
+read raw
+
+read size
+
+root
+
+root dir
+
+root directory
+
+security
+
+server string
+
+smbrun
+
+socket options
+
+status
+
+strip dot
+
+time offset
+
+username map
+
+use rhosts
+
+valid chars
+
+workgroup
+
+write raw
+
+.SS COMPLETE LIST OF SERVICE PARAMETER
+
+Here is a list of all service parameters. See the section of each
+parameter for details. Note that some are synonyms.
+
+admin users
+
+allow hosts
+
+alternate permissions
+
+available
+
+browseable
+
+case sensitive
+
+case sig names
+
+copy
+
+create mask
+
+create mode
+
+comment
+
+default case
+
+deny hosts
+
+directory
+
+dont descend
+
+exec
+
+force group
+
+force user
+
+guest account
+
+guest ok
+
+guest only
+
+hide dot files
+
+hosts allow
+
+hosts deny
+
+invalid users
+
+locking
+
+lppause command
+
+lpq command
+
+lpresume command
+
+lprm command
+
+magic output
+
+magic script
+
+mangle case
+
+mangled names
+
+mangling char
+
+map archive
+
+map hidden
+
+map system
+
+max connections
+
+min print space
+
+only guest
+
+only user
+
+path
+
+postexec
+
+postscript
+
+preserve case
+
+print command
+
+print ok
+
+printable
+
+printer
+
+printer name
+
+public
+
+read only
+
+read list
+
+revalidate
+
+root postexec
+
+root preexec
+
+set directory
+
+share modes
+
+short preserve case
+
+strict locking
+
+sync always
+
+user
+
+username
+
+users
+
+valid users
+
+volume
+
+wide links
+
+writable
+
+write ok
+
+writeable
+
+write list
+
+.SS EXPLANATION OF EACH PARAMETER
+.RS 3
+
+.SS admin users (G)
+
+This is a list of users who will be granted administrative privilages
+on the share. This means that they will do all file operations as the
+super-user (root).
+
+You should use this option very carefully, as any user in this list
+will be able to do anything they like on the share, irrespective of
+file permissions.
+
+.B Default:
+       no admin users
+
+.B Example:
+       admin users = jason
+
+.SS auto services (G)
+This is a list of services that you want to be automatically added to
+the browse lists. This is most useful for homes and printers services
+that would otherwise not be visible.
+
+Note that if you just want all printers in your printcap file loaded
+then the "load printers" option is easier.
+
+.B Default:
+       no auto services
+
+.B Example:
+       auto services = fred lp colorlp
+
+
+.SS allow hosts (S)
+A synonym for this parameter is 'hosts allow'.
+
+This parameter is a comma delimited set of hosts which are permitted to access
+a services. If specified in the [global] section, matching hosts will be
+allowed access to any service that does not specifically exclude them from
+access. Specific services my have their own list, which override those
+specified in the [global] section.
+
+You can specify the hosts by name or IP number. For example, you could
+restrict access to only the hosts on a Class C subnet with something like
+"allow hosts = 150.203.5.". The full syntax of the list is described in
+the man page
+.B hosts_access(5).
+
+You can also specify hosts by network/netmask pairs and by netgroup
+names if your system supports netgroups. The EXCEPT keyword can also
+be used to limit a wildcard list. The following examples may provide
+some help:
+
+Example 1: allow all IPs in 150.203.*.* except one
+
+       hosts allow = 150.203. EXCEPT 150.203.6.66
+
+Example 2: allow hosts that match the given network/netmask
+
+       hosts allow = 150.203.15.0/255.255.255.0
+
+Example 3: allow a couple of hosts
+
+       hosts allow = lapland, arvidsjaur
+
+Example 4: allow only hosts in netgroup "foonet" or localhost, but 
+deny access from one particular host
+
+       hosts allow = @foonet, localhost
+       hosts deny = pirate
+
+Note that access still requires suitable user-level passwords.
+
+See testparm(1) for a way of testing your host access to see if it
+does what you expect.
+
+.B Default:
+       none (ie., all hosts permitted access)
+
+.B Example:
+       allow hosts = 150.203.5. myhost.mynet.edu.au
+
+.SS alternate permissions (S)
+
+This option affects the way the "read only" DOS attribute is produced
+for unix files. If this is false then the read only bit is set for
+files on writeable shares which the user cannot write to.
+
+If this is true then it is set for files whos user write bit is not set.
+
+The latter behaviour of useful for when users copy files from each
+others directories, and use a file manager that preserves
+permissions. Without this option they may get annoyed as all copied
+files will have the "read only" bit set.
+
+.B Default:
+       alternate permissions = no
+
+.B Example:
+       alternate permissions = yes
+
+.SS available (S)
+This parameter lets you 'turn off' a service. If 'available = no', then
+ALL attempts to connect to the service will fail. Such failures are logged.
+
+.B Default:
+       available = yes
+
+.B Example:
+       available = no
+.SS browseable (S)
+This controls whether this share is seen in the list of available
+shares in a net view and in the browse list.
+
+.B Default:
+       browseable = Yes
+
+.B Example: 
+       browseable = No
+
+.SS case sig names (G)
+See "case sensitive"
+
+.SS comment (S)
+This is a text field that is seen when a client does a net view to
+list what shares are available. It will also be used when browsing is
+fully supported.
+
+.B Default:
+       No comment string
+
+.B Example:
+       comment = Fred's Files
+
+.SS config file (G)
+
+This allows you to override the config file to use, instead of the
+default (usually smb.conf). There is a chicken and egg problem here as
+this option is set in the config file! 
+
+For this reason, if the name of the config file has changed when the
+parameters are loaded then it will reload them from the new config
+file.
+
+This option takes the usual substitutions, which can be very useful.
+
+If thew config file doesn't exist then it won't be loaded (allowing
+you to special case the config files of just a few clients).
+
+.B Example:
+       config file = /usr/local/samba/smb.conf.%m
+
+.SS copy (S)
+This parameter allows you to 'clone' service entries. The specified
+service is simply duplicated under the current service's name. Any 
+parameters specified in the current section will override those in the
+section being copied.
+
+This feature lets you set up a 'template' service and create similar 
+services easily. Note that the service being copied must occur earlier 
+in the configuration file than the service doing the copying.
+
+.B Default:
+       none
+
+.B Example:
+       copy = otherservice
+.SS create mask (S)
+A synonym for this parameter is 'create mode'.
+
+This parameter is the octal modes which are used when converting DOS modes 
+to Unix modes.
+
+Note that Samba will or this value with 0700 as you must have at least
+user read, write and execute for Samba to work properly.
+
+.B Default:
+       create mask = 0755
+
+.B Example:
+       create mask = 0775
+.SS create mode (S)
+See
+.B create mask.
+.SS dead time (G)
+The value of the parameter (a decimal integer) represents the number of
+minutes of inactivity before a connection is considered dead, and it
+is disconnected. The deadtime only takes effect if the number of open files
+is zero.
+
+This is useful to stop a server's resources being exhausted by a large
+number of inactive connections.
+
+Most clients have an auto-reconnect feature when a connection is broken so
+in most cases this parameter should be transparent to users.
+
+Using this parameter with a timeout of a few minutes is recommended
+for most systems.
+
+A deadtime of zero indicates that no auto-disconnection should be performed.
+
+.B Default:
+       dead time = 0
+
+.B Example:
+       dead time = 15
+.SS debug level (G)
+The value of the parameter (an integer) allows the debug level
+(logging level) to be specified in the smb.conf file. This is to give
+greater flexibility in the configuration of the system.
+
+The default will be the debug level specified on the command line.
+
+.B Example:
+       debug level = 3
+.SS default (G)
+See
+.B default service.
+.SS default case (S)
+
+See the section on "NAME MANGLING" Also note the addition of "short
+preserve case"
+
+.SS default service (G)
+A synonym for this parameter is 'default'.
+
+This parameter specifies the name of a service which will be connected to
+if the service actually requested cannot be found. Note that the square
+brackets are NOT given in the parameter value (see example below).
+
+There is no default value for this parameter. If this parameter is not given,
+attempting to connect to a nonexistent service results in an error.
+
+Typically the default service would be a public, read-only service.
+
+Also not that s of 1.9.14 the apparent service name will be changed to
+equal that of the requested service, this is very useful as it allows
+you to use macros like %S to make a wildcard service.
+
+Note also that any _ characters in the name of the service used in the
+default service will get mapped to a /. This allows for interesting
+things.
+
+
+.B Example:
+       default service = pub
+        
+        [pub]
+             path = /%S
+          
+
+.SS deny hosts (S)
+A synonym for this parameter is 'hosts deny'.
+
+The opposite of 'allow hosts' - hosts listed here are NOT permitted
+access to services unless the specific services have their own lists to
+override this one. Where the lists conflict, the 'allow' list takes precedence.
+
+.B Default:
+       none (ie., no hosts specifically excluded)
+
+.B Example:
+       deny hosts = 150.203.4. badhost.mynet.edu.au
+.SS dfree command (G)
+The dfree command setting should only be used on systems where a
+problem occurs with the internal disk space calculations. This has
+been known to happen with Ultrix, but may occur with other operating
+systems. The symptom that was seen was an error of "Abort Retry
+Ignore" at the end of each directory listing.
+
+This setting allows the replacement of the internal routines to
+calculate the total disk space and amount available with an external
+routine. The example below gives a possible script that might fulfill
+this function. 
+
+The external program will be passed a single parameter indicating a
+directory in the filesystem being queried. This will typically consist
+of the string "./". The script should return two integers in ascii. The
+first should be the total disk space in blocks, and the second should
+be the number of available blocks. An optional third return value
+can give the block size in bytes. The default blocksize is 1024 bytes.
+
+Note: Your script should NOT be setuid or setgid and should be owned by
+(and writable only by) root!
+
+.B Default:
+       By default internal routines for determining the disk capacity
+and remaining space will be used.
+
+.B Example:
+       dfree command = /usr/local/smb/dfree
+
+       Where the script dfree (which must be made executable) could be
+
+       #!/bin/sh
+       df $1 | tail -1 | awk '{print $2" "$4}'
+
+       or perhaps (on Sys V)
+
+       #!/bin/sh
+       /usr/bin/df -k $1 | tail -1 | awk '{print $3" "$5}'
+
+
+       Note that you may have to replace the command names with full
+path names on some systems.
+.SS directory (S)
+See
+.B path.
+.SS dont descend (S)
+There are certain directories on some systems (eg., the /proc tree under
+Linux) that are either not of interest to clients or are infinitely deep
+(recursive). This parameter allows you to specify a comma-delimited list
+of directories that the server should always show as empty.
+
+Note that Samba can be very fussy about the exact format of the "dont
+descend" entries. For example you ma need "./proc" instead of just
+"/proc". Experimentation is the best policy :-)
+
+.B Default:
+       none (ie., all directories are OK to descend)
+
+.B Example:
+       dont descend = /proc,/dev
+
+.SS encrypt passwords (G)
+
+This boolean controls whether encrypted passwords will be negotiated
+with the cient. Note that this option has no effect if you haven't
+compiled in the necessary des libraries and encryption code. It
+defaults to no.
+
+.SS exec (S)
+
+This is an alias for preexec
+
+
+.SS force group (S)
+This specifies a group name that all connections to this service
+should be made as. This may be useful for sharing files.
+
+.B Default:
+       no forced group
+
+.B Example:
+       force group = agroup
+
+.SS force user (S)
+This specifies a user name that all connections to this service
+should be made as. This may be useful for sharing files. You should
+also use it carefully as using it incorrectly can cause security
+problems.
+
+This user name only gets used once a connection is established. Thus
+clients still need to connect as a valid user and supply a valid
+password. Once connected, all file operations will be performed as the
+"forced user", not matter what username the client connected as.
+
+.B Default:
+       no forced user
+
+.B Example:
+       force user = auser
+
+.SS guest account (S)
+This is a username which will be used for access to services which are
+specified as 'guest ok' (see below). Whatever privileges this user has
+will be available to any client connecting to the guest
+service. Typically this user will exist in the password file, but will
+not have a valid login. If a username is specified in a given service,
+the specified username overrides this one.
+
+One some systems the account "nobody" may not be able to print. Use
+another account in this case. You should test this by trying to log in
+as your guest user (perhaps by using the "su -" command) and trying to
+print using lpr.
+
+Note that as of version 1.9 of Samba this option may be set
+differently for each service.
+
+.B Default:
+       specified at compile time
+
+.B Example:
+       guest account = nobody
+.SS getwd cache (G)
+This is a tuning option. When this is enabled a cacheing algorithm will
+be used to reduce the time taken for getwd() calls. This can have a
+significant impact on performance, especially when widelinks is False.
+
+.B Default:
+       getwd cache = No
+
+.B Example:
+       getwd cache = Yes
+.SS guest ok (S)
+See
+.B public.
+.SS guest only (S)
+If this parameter is 'yes' for a service, then only guest connections to the
+service are permitted. This parameter will have no affect if "guest ok" or
+"public" is not set for the service.
+
+See the section below on user/password validation for more information about
+this option.
+
+.B Default:
+       guest only = no
+
+.B Example:
+       guest only = yes
+.SS hide dot files (S)
+This is a boolean parameter that controls whether files starting with
+a dot appear as hidden files.
+
+.B Default:
+       hide dot files = yes
+
+.B Example:
+       hide dot files = no
+.SS hosts allow (S)
+See
+.B allow hosts.
+.SS hosts deny (S)
+See
+.B deny hosts.
+
+.SS group (S)
+This is an alias for "force group" and is only kept for compatability
+with old versions of Samba. It may be removed in future versions.
+
+.SS hosts equiv (G)
+If this global parameter is a non-null string, it specifies the name of
+a file to read for the names of hosts and users who will be allowed access
+without specifying a password.
+
+This is not be confused with 
+.B allow hosts
+which is about hosts access to services and is more useful for guest services.
+.B hosts equiv
+may be useful for NT clients which will not supply passwords to samba.
+
+NOTE: The use of hosts.equiv can be a major security hole. This is
+because you are trusting the PC to supply the correct username. It is
+very easy to get a PC to supply a false username. I recommend that the
+hosts.equiv option be only used if you really know what you are doing,
+or perhaps on a home network where you trust your wife and kids :-)
+
+.B Default
+       No host equivalences
+
+.B Example
+       hosts equiv = /etc/hosts.equiv
+
+.SS invalid users (S)
+This is a list of users that should not be allowed to login to this
+service. This is really a "paranoid" check to absolutely ensure an
+improper setting does not breach your security.
+
+A name starting with @ is interpreted as a unix group.
+
+The current servicename is substituted for %S. This is useful in the
+[homes] section.
+
+See also "valid users"
+
+.B Default
+       No invalid users
+
+.B Example
+       invalid users = root fred admin @wheel
+
+.SS include (G)
+
+This allows you to inlcude one config file inside another. the file is
+included literally, as though typed in place.
+
+It takes the standard substitutions, except %u, %P and %S
+
+.SS keep alive (G)
+The value of the parameter (an integer) represents the number of seconds 
+between 'keepalive' packets. If this parameter is zero, no keepalive packets
+will be sent. Keepalive packets, if sent, allow the server to tell whether a
+client is still present and responding.
+
+Keepalives should, in general, not be needed if the socket being used
+has the SO_KEEPALIVE attribute set on it (see "socket
+options"). Basically you should only use this option if you strike
+difficulties.
+
+.B Default:
+       keep alive = 0
+
+.B Example:
+       keep alive = 60
+.SS load printers (G)
+A boolean variable that controls whether all printers in the printcap
+will be loaded for browsing by default. 
+
+.B Default:
+       load printers = no
+
+.B Example:
+       load printers = yes
+
+.SS lock directory (G)
+This options specifies the directory where lock files will be placed.
+The lock files are used to implement the "max connections" option.
+
+.B Default:
+       lock directory = /tmp/samba
+
+.B Example: 
+       lock directory = /usr/local/samba/locks
+.SS locking (S)
+This controls whether or not locking will be performed by the server in 
+response to lock requests from the client.
+
+If "locking = no", all lock and unlock requests will appear to succeed and 
+all lock queries will indicate that the queried lock is clear.
+
+If "locking = yes", real locking will be performed by the server.
+
+This option may be particularly useful for read-only filesystems which
+do not need locking (such as cdrom drives).
+
+Be careful about disabling locking either globally or in a specific
+service, as lack of locking may result in data corruption.
+
+.B Default:
+       locking = yes
+
+.B Example:
+       locking = no
+
+.SS log file (G)
+
+This options allows you to override the name of the Samba log file
+(also known as the debug file).
+
+This option takes the standard substitutions, allowing you to have
+separate log files for each user or machine.
+
+.B Example:
+       log file = /usr/local/samba/log.%m
+
+.SS log level (G)
+see "debug level"
+
+.SS lppause command (S)
+This parameter specifies the command to be executed on the server host in
+order to stop printing or spooling a specific print job.
+
+This command should be a program or script which takes a printer name and
+job number to pause the print job. Currently I don't know of any print
+spooler system that can do this with a simple option, except for the PPR
+system from Trinity College (ppr\-dist.trincoll.edu/pub/ppr). One way
+of implementing this is by using job priorities, where jobs having a too
+low priority wont be sent to the printer. See also the lppause command.
+
+If a %p is given then the printername is put in it's place. A %j is
+replaced with the job number (an integer).
+On HPUX (see printing=hpux), if the -p%p option is added to the lpq
+command, the job will show up with the correct status, i.e. if the job
+priority is lower than the set fence priority it will have the PAUSED
+status, whereas if the priority is equal or higher it will have the
+SPOOLED or PRINTING status.
+
+Note that it is good practice to include the absolute path in the lppause
+command as the PATH may not be available to the server.
+
+.B Default:
+        Currently no default value is given to this string
+
+.B Example for HPUX:
+        lppause command = /usr/bin/lpalt %p-%j -p0
+
+.SS lpq cache time (G)
+
+This controls how long lpq info will be cached for to prevent the lpq
+command being called too often. A separate cache is kept for each
+variation of the lpq command used by the system, so if you use
+different lpq commands for different users then they won't share cache
+information.
+
+The cache files are stored in /tmp/lpq.xxxx where xxxx is a hash
+of the lpq command in use.
+
+The default is 10 seconds, meaning that the cached results of a
+previous identical lpq command will be used if the cached data is less
+than 10 seconds old. A large value may be advisable if your lpq
+command is very slow.
+
+A value of 0 will disable cacheing completely.
+
+.B Default:
+       lpq cache time = 10
+
+.B Example:
+       lpq cache time = 30
+
+.SS lpq command (S)
+This parameter specifies the command to be executed on the server host in
+order to obtain "lpq"-style printer status information. 
+
+This command should be a program or script which takes a printer name
+as its only parameter and outputs printer status information. 
+
+Currently four styles of printer status information are supported;
+BSD, SYSV, AIX and HPUX. This covers most unix systems. You control
+which type is expected using the "printing =" option.
+
+Some clients (notably Windows for Workgroups) may not correctly send the
+connection number for the printer they are requesting status information
+about. To get around this, the server reports on the first printer service
+connected to by the client. This only happens if the connection number sent
+is invalid.
+
+If a %p is given then the printername is put in it's place. Otherwise
+it is placed at the end of the command.
+
+Note that it is good practice to include the absolute path in the lpq
+command as the PATH may not be available to the server.
+
+.B Default:
+        depends on the setting of "printing ="
+
+.B Example:
+       lpq command = /usr/bin/lpq %p
+
+.SS lpresume command (S)
+This parameter specifies the command to be executed on the server host in
+order to restart or continue printing or spooling a specific print job.
+
+This command should be a program or script which takes a printer name and
+job number to resume the print job. See also the lppause command.
+
+If a %p is given then the printername is put in it's place. A %j is
+replaced with the job number (an integer).
+
+Note that it is good practice to include the absolute path in the lpresume
+command as the PATH may not be available to the server.
+
+.B Default:
+        Currently no default value is given to this string
+
+.B Example for HPUX:
+        lpresume command = /usr/bin/lpalt %p-%j -p2
+
+.SS lprm command (S)
+This parameter specifies the command to be executed on the server host in
+order to delete a print job.
+
+This command should be a program or script which takes a printer name
+and job number, and deletes the print job.
+
+Currently four styles of printer control are supported; BSD, SYSV, AIX
+and HPUX. This covers most unix systems. You control which type is
+expected using the "printing =" option.
+
+If a %p is given then the printername is put in it's place. A %j is
+replaced with the job number (an integer).
+
+Note that it is good practice to include the absolute path in the lprm
+command as the PATH may not be available to the server.
+
+.B Default:
+        depends on the setting of "printing ="
+
+.B Example 1:
+       lprm command = /usr/bin/lprm -P%p %j
+
+.B Example 2:
+       lprm command = /usr/bin/cancel %p-%j
+
+.SS magic output (S)
+This parameter specifies the name of a file which will contain output
+created by a magic script (see
+.I magic script
+below).
+
+Warning: If two clients use the same magic script in the same directory the
+output file content is undefined.
+.B Default:
+       magic output = <magic script name>.out
+
+.B Example:
+       magic output = myfile.txt
+.SS magic script (S)
+This parameter specifies the name of a file which, if opened, will be
+executed by the server when the file is closed. This allows a Unix script
+to be sent to the Samba host and executed on behalf of the connected user.
+
+Scripts executed in this way will be deleted upon completion, permissions
+permitting.
+
+If the script generates output, output will be sent to the file specified by
+the
+.I magic output
+parameter (see above).
+
+Note that some shells are unable to interpret scripts containing
+carriage-return-linefeed instead of linefeed as the end-of-line
+marker. Magic scripts must be executable "as is" on the host, which
+for some hosts and some shells will require filtering at the DOS end.
+
+Magic scripts are EXPERIMENTAL and should NOT be relied upon.
+.B Default:
+       None. Magic scripts disabled.
+
+.B Example:
+       magic script = user.csh
+.SS mangled map (S)
+This is for those who want to directly map UNIX file names which are
+not representable on DOS.  The mangling of names is not always what is
+needed.  In particular you may have documents with file extensiosn
+that differ between dos and unix. For example, under unix it is common
+to use .html for HTML files, whereas under dos .htm is more commonly
+used.
+
+So to map 'html' to 'htm' you put:
+
+  mangled map = (*.html *.htm)
+
+One very useful case is to remove the annoying ;1 off the ends of
+filenames on some CDROMS (only visible under some unixes). To do this
+use a map of (*;1 *)
+
+.B default:
+       no mangled map
+
+.B Example:
+       mangled map = (*;1 *)
+
+.SS mangle case (S)
+
+See the section on "NAME MANGLING"
+
+.SS mangled names (S)
+This controls whether non-DOS names under Unix should be mapped to
+DOS-compatible names ("mangled") and made visible, or whether non-DOS names
+should simply be ignored.
+
+See the section on "NAME MANGLING" for details on how to control the
+mangling process.
+
+If mangling is used then the mangling algorithm is as follows:
+.RS
+- the first (up to) five alphanumeric characters before the rightmost dot of
+the filename are preserved, forced to upper case, and appear as the first (up
+to) five characters of the mangled name.
+
+- a tilde ("~") is appended to the first part of the mangled name, followed
+by a two-character unique sequence, based on the origonal root name 
+(i.e., the original filename minus its final extension). The final
+extension is included in the hash calculation only if it contains any upper
+case characters or is longer than three characters.
+
+Note that the character to use may be specified using the "mangling
+char" option, if you don't like ~.
+
+- the first three alphanumeric characters of the final extension are preserved,
+forced to upper case and appear as the extension of the mangled name. The 
+final extension is defined as that part of the original filename after the
+rightmost dot. If there are no dots in the filename, the mangled name will
+have no extension (except in the case of hidden files - see below).
+
+- files whose Unix name begins with a dot will be presented as DOS hidden
+files. The mangled name will be created as for other filenames, but with the
+leading dot removed and "___" as its extension regardless of actual original
+extension (that's three underscores).
+.RE
+
+The two-digit hash value consists of upper case alphanumeric characters.
+
+This algorithm can cause name collisions only if files in a directory share
+the same first five alphanumeric characters. The probability of such a clash 
+is 1/1300.
+
+The name mangling (if enabled) allows a file to be copied between Unix
+directories from DOS while retaining the long Unix filename. Unix files can
+be renamed to a new extension from DOS and will retain the same basename. 
+Mangled names do not change between sessions.
+
+.B Default:
+       mangled names = yes
+
+.B Example:
+       mangled names = no
+.SS mangling char (S)
+This controls what character is used as the "magic" character in name
+mangling. The default is a ~ but this may interfere with some
+software. Use this option to set it to whatever you prefer.
+
+.B Default:
+       mangling char = ~
+
+.B Example:
+       mangling char = ^
+
+.SS max log size (G)
+
+This option (an integer in kilobytes) specifies the max size the log
+file should grow to. Samba periodically checks the size and if it is
+exceeded it will rename the file, adding a .old extension.
+
+A size of 0 means no limit.
+
+.B Default:
+       max log size = 5000
+
+.B Example:
+       max log size = 1000
+
+.SS max xmit (G)
+
+This option controls the maximum packet size that will be negotiated
+by Samba. The default is 65535, which is the maximum. In some cases
+you may find you get better performance with a smaller value. A value
+below 2048 is likely to cause problems.
+
+.B Default:
+       max xmit = 65535
+
+.B Example:
+       max xmit = 8192
+
+.SS mangled stack (G)
+This parameter controls the number of mangled names that should be cached in
+the Samba server.
+
+This stack is a list of recently mangled base names (extensions are only
+maintained if they are longer than 3 characters or contains upper case
+characters).
+
+The larger this value, the more likely it is that mangled names can be
+successfully converted to correct long Unix names. However, large stack
+sizes will slow most directory access. Smaller stacks save memory in the
+server (each stack element costs 256 bytes).
+
+It is not possible to absolutely guarantee correct long file names, so
+be prepared for some surprises!
+
+.B Default:
+       mangled stack = 50
+
+.B Example:
+       mangled stack = 100
+
+.SS map archive (S)
+This controls whether the DOS archive attribute should be mapped to Unix
+execute bits.  The DOS archive bit is set when a file has been modified
+since its last backup.  One motivation for this option it to keep Samba/your
+PC from making any file it touches from becoming executable under UNIX.
+This can be quite annoying for shared source code, documents,  etc...
+
+.B Default:
+      map archive = yes
+
+.B Example:
+      map archive = no
+
+.SS map hidden (S)
+This controls whether DOS style hidden files should be mapped to Unix
+execute bits.
+
+.B Default:
+       map hidden = no
+
+.B Example:
+       map hidden = yes
+.SS map system (S)
+This controls whether DOS style system files should be mapped to Unix
+execute bits.
+
+.B Default:
+       map system = no
+
+.B Example:
+       map system = yes
+.SS max connections (S)
+This option allows the number of simultaneous connections to a
+service to be limited. If "max connections" is greater than 0 then
+connections will be refused if this number of connections to the
+service are already open. A value of zero mean an unlimited number of
+connections may be made.
+
+Record lock files are used to implement this feature. The lock files
+will be stored in the directory specified by the "lock directory" option.
+
+.B Default:
+       max connections = 0
+
+.B Example:
+       max connections = 10
+.SS only user (S)
+This is a boolean option that controls whether connections with
+usernames not in the user= list will be allowed. By default this
+option is disabled so a client can supply a username to be used by
+the server.
+
+Note that this also means Samba won't try to deduce usernames from the
+service name. This can be annoying for the [homes] section. To get
+around this you could use "user = %S" which means your "user" list
+will be just the service name, which for home directories is the name
+of the user.
+
+.B Default: 
+       only user = False
+
+.B Example: 
+       only user = True
+
+.SS message command (G)
+
+This specifies what command to run when the server receives a WinPopup
+style message.
+
+This would normally be a command that would deliver the message
+somehow. How this is to be done is up to your imagination.
+
+What I use is:
+
+   message command = csh -c 'xedit %s;rm %s' &
+
+This delivers the message using xedit, then removes it
+afterwards. NOTE THAT IT IS VERY IMPORTANT THAT THIS COMMAND RETURN
+IMMEDIATELY. That's why I have the & on the end. If it doesn't return
+immediately then your PCs may freeze when sending messages (they
+should recover after 30secs, hopefully).
+
+All messages are delivered as the global guest user. The command takes
+the standard substitutions, although %u won't work (%U may be better
+in this case).
+
+Apart from the standard substitutions, some additional ones apply. In
+particular:
+
+%s = the filename containing the message
+
+%t = the destination that the message was sent to (probably the server
+name)
+
+%f = who the message is from
+
+You could make this command send mail, or whatever else takes your
+fancy. Please let me know of any really interesting ideas you have.
+
+Here's a way of sending the messages as mail to root:
+
+message command = /bin/mail -s 'message from %f on %m' root < %s; rm %s
+
+If you don't have a message command then the message won't be
+delivered and Samba will tell the sender there was an
+error. Unfortunately WfWg totally ignores the error code and carries
+on regardless, saying that the message was delivered.
+
+If you want to silently delete it then try "message command = rm %s".
+
+For the really adventurous, try something like this:
+
+message command = csh -c 'csh < %s |& /usr/local/samba/smbclient \\
+                  -M %m; rm %s' &
+
+this would execute the command as a script on the server, then give
+them the result in a WinPopup message. Note that this could cause a
+loop if you send a message from the server using smbclient! You better
+wrap the above in a script that checks for this :-)
+
+.B Default:
+       no message command
+
+.B Example:
+        message command = csh -c 'xedit %s;rm %s' &
+
+.SS min print space (S)
+
+This sets the minimum amount of free disk space that must be available
+before a user will be able to spool a print job. It is specified in
+kilobytes. The default is 0, which means no limit.
+
+.B Default:
+       min print space = 0
+
+.B Example:
+       min print space = 2000
+
+.SS null passwords (G)
+Allow or disallow access to accounts that have null passwords. 
+
+.B Default:
+       null passwords = no
+
+.B Example:
+       null passwords = yes
+
+.SS os level (G)
+This integer value controls what level Samba advertises itself as for
+browse elections. See BROWSING.txt for details.
+
+.SS packet size (G)
+The maximum transmit packet size during a raw read. This option is no
+longer implemented as of version 1.7.00, and is kept only so old
+configuration files do not become invalid.
+
+.SS passwd chat (G)
+This string coontrols the "chat" conversation that takes places
+between smbd and the local password changing program to change the
+users password. The string describes a sequence of response-receive
+pairs that smbd uses to determine what to send to the passwd program
+and what to expect back. If the expected output is not received then
+the password is not changed.
+
+This chat sequence is often quite site specific, deppending on what
+local methods are used for password control (such as NIS+ etc).
+
+The string can contain the macros %o and %n which are substituted for
+the old and new passwords respectively. It can aso contain the
+standard macros \\n \\r \\t and \\s to give line-feed, carriage-return,
+tab and space.
+
+The string can also contain a * which matches any sequence of
+characters.
+
+Double quotes can be used to collect strings with spaces in them into
+a single string.
+
+If the send string in any part of the chat sequence is a fullstop "."
+then no string is sent. Similarly, is the expect string is a fullstop
+then no string is expected.
+
+.B Example:
+       passwd chat = "*Enter OLD password*" %o\\n "*Enter NEW password*" %n\\n \\
+                      "*Reenter NEW password*" %n\\n "*Password changed*"
+
+.B Default:
+       passwd chat = *old*password* %o\\n *new*password* %n\\n *new*password* %n\\n *changed*
+
+.SS passwd program (G)
+The name of a program that can be used to set user passwords.
+
+This is only necessary if you have enabled remote password changing at
+compile time. Any occurances of %u will be replaced with the user
+name.
+
+Also note that many passwd programs insist in "reasonable" passwords,
+such as a minimum length, or the inclusion of mixed case chars and
+digits. This can pose a problem as some clients (such as Windows for
+Workgroups) uppercase the password before sending it. 
+
+.B Default:
+       passwd program = /bin/passwd
+
+.B Example:
+       passwd program = /sbin/passwd %u
+
+.SS password level (G)
+Some client/server conbinations have difficulty with mixed-case passwords.
+One offending client is Windows for Workgroups, which for some reason forces
+passwords to upper case when using the LANMAN1 protocol, but leaves them alone
+when using COREPLUS!
+
+This parameter defines the maximum number of characters that may be upper case
+in passwords.
+
+For example, say the password given was "FRED". If
+.B password level
+is set to 1 (one), the following combinations would be tried if "FRED" failed:
+"Fred", "fred", "fRed", "frEd", "freD". If
+.B password level was set to 2 (two), the following combinations would also be
+tried: "FRed", "FrEd", "FreD", "fREd", "fReD", "frED". And so on.
+
+The higher value this parameter is set to the more likely it is that a mixed
+case password will be matched against a single case password. However, you
+should be aware that use of this parameter reduces security and increases the
+time taken to process a new connection.
+
+A value of zero will cause only two attempts to be made - the password as is
+and the password in all-lower case.
+
+If you find the connections are taking too long with this option then
+you probably have a slow crypt() routine. Samba now comes with a fast
+"ufc crypt" that you can select in the Makefile. You should also make
+sure the PASSWORD_LENGTH option is correct for your system in local.h
+and includes.h. On most systems only the first 8 chars of a password
+are significant so PASSWORD_LENGTH should be 8, but on some longer
+passwords are significant. The inlcudes.h file tries to select the
+right length for your system.
+
+.B Default:
+       password level = 0
+
+.B Example:
+       password level = 4
+
+.SS password server (G)
+
+By specifying the name of another SMB server (such as a WinNT box)
+with this option, and using "security = server" you can get Samba to
+do all it's username/password validation via a remote server.
+
+This options sets the name of the password server to use. It must be a
+netbios name, so if the machines netbios name is different from it's
+internet name then you may have to add it's netbios name to
+/etc/hosts.
+
+The password server much be a machine capable of using the "LM1.2X002"
+or the "LM NT 0.12" protocol, and it must be in user level security
+mode. 
+
+NOTE: Using a password server means your unix box (running Samba) is
+only as secure as your password server. DO NOT CHOOSE A PASSWORD
+SERVER THAT YOU DON'T COMPLETELY TRUST.
+
+Never point a Samba server at itself for password serving. This will
+cause a loop and could lock up your Samba server!
+
+The name of the password server takes the standard substitutions, but
+probably the only useful one is %m, which means the Samba server will
+use the incoming client as the password server. If you use this then
+you better trust your clients, and you better restrict them with hosts
+allow!
+
+If you list several hosts in the "password server" option then smbd
+will try each in turn till it finds one that responds. This is useful
+in case your primary server goes down.
+
+.SS path (S)
+A synonym for this parameter is 'directory'.
+
+This parameter specifies a directory to which the user of the service is to
+be given access. In the case of printable services, this is where print data 
+will spool prior to being submitted to the host for printing.
+
+For a printable service offering guest access, the service should be readonly
+and the path should be world-writable and have the sticky bit set. This is not
+mandatory of course, but you probably won't get the results you expect if you
+do otherwise.
+
+Any occurances of %u in the path will be replaced with the username
+that the client is connecting as. Any occurances of %m will be
+replaced by the name of the machine they are connecting from. These
+replacements are very useful for setting up pseudo home directories
+for users.
+
+Note that this path will be based on 'root dir' if one was specified.
+.B Default:
+       none
+
+.B Example:
+       path = /home/fred+ 
+
+.SS postexec (S)
+
+This option specifies a command to be run whenever the service is
+disconnected. It takes the usual substitutions. The command may be run
+as the root on some systems.
+
+An interesting example may be do unmount server resources:
+
+postexec = /etc/umount /cdrom
+
+See also preexec
+
+.B Default:
+      none (no command executed)
+
+.B Example:
+      postexec = echo \"%u disconnected from %S from %m (%I)\" >> /tmp/log
+
+.SS postscript (S)
+This parameter forces a printer to interpret the print files as
+postscript. This is done by adding a %! to the start of print output. 
+
+This is most useful when you have lots of PCs that persist in putting
+a control-D at the start of print jobs, which then confuses your
+printer.
+
+.B Default: 
+       postscript = False
+
+.B Example: 
+       postscript = True
+
+.SS preexec (S)
+
+This option specifies a command to be run whenever the service is
+connected to. It takes the usual substitutions.
+
+An interesting example is to send the users a welcome message every
+time they log in. Maybe a message of the day? Here is an example:
+
+preexec = csh -c 'echo \"Welcome to %S!\" | \
+       /usr/local/samba/smbclient -M %m -I %I' &
+
+Of course, this could get annoying after a while :-)
+
+See also postexec
+
+.B Default:
+       none (no command executed)
+
+.B Example:
+       preexec = echo \"%u connected to %S from %m (%I)\" >> /tmp/log
+
+.SS preferred master (G)
+This boolean parameter controls if Samba is a preferred master browser
+for its workgroup. Setting this gives it a slight edge in elections
+and also means it will automatically start an election when it starts
+up. 
+
+It is on by default.
+
+.SS preload
+This is an alias for "auto services"
+
+.SS preserve case (S)
+
+This controls if new filenames are created with the case that the
+client passes, or if they are forced to be the "default" case.
+
+.B Default:
+       preserve case = no
+
+See the section on "NAME MANGLING" for a fuller discussion.
+
+.SS print command (S)
+After a print job has finished spooling to a service, this command will be
+used via a system() call to process the spool file. Typically the command 
+specified will submit the spool file to the host's printing subsystem, but
+there is no requirement that this be the case. The server will not remove the
+spool file, so whatever command you specify should remove the spool file when
+it has been processed, otherwise you will need to manually remove old spool
+files.
+
+The print command is simply a text string. It will be used verbatim,
+with two exceptions: All occurrences of "%s" will be replaced by the
+appropriate spool file name, and all occurrences of "%p" will be
+replaced by the appropriate printer name. The spool file name is
+generated automatically by the server, the printer name is discussed
+below.
+
+The full path name will be used for the filename if %s is not preceded
+by a /. If you don't like this (it can stuff up some lpq output) then
+use %f instead. Any occurances of %f get replaced by the spool
+filename without the full path at the front.
+
+The print command MUST contain at least one occurrence of "%s" or %f -
+the "%p" is optional. At the time a job is submitted, if no printer
+name is supplied the "%p" will be silently removed from the printer
+command.
+
+If specified in the [global] section, the print command given will be used
+for any printable service that does not have its own print command specified.
+
+If there is neither a specified print command for a printable service nor a 
+global print command, spool files will be created but not processed and (most
+importantly) not removed.
+
+Note that printing may fail on some unixes from the "nobody"
+account. If this happens then create an alternative guest account that
+can print and set the "guest account" in the [global] section.
+
+You can form quite complex print commands by realising that they are
+just passed to a shell. For example the following will log a print
+job, print the file, then remove it. Note that ; is the usual
+separator for command in shell scripts.
+
+print command = echo Printing %s >> /tmp/print.log; lpr -P %p %s; rm %s
+
+You may have to vary this command considerably depending on how you
+normally print files on your system.
+
+.B Default:
+        print command = lpr -r -P %p %s
+
+.B Example:
+       print command = /usr/local/samba/myprintscript %p %s
+.SS print ok (S)
+See
+.B printable.
+.SS printable (S)
+A synonym for this parameter is 'print ok'.
+
+If this parameter is 'yes', then clients may open, write to and submit spool 
+files on the directory specified for the service.
+
+Note that a printable service will ALWAYS allow writing to the service path
+(user privileges permitting) via the spooling of print data. The 'read only'
+parameter controls only non-printing access to the resource.
+
+.B Default:
+       printable = no
+
+.B Example:
+       printable = yes
+
+.SS printing (G)
+This parameters controls how printer status information is interpreted
+on your system, and also affects the default values for the "print
+command", "lpq command" and "lprm command".
+
+Currently three printing styles are supported. They are "printing =
+bsd", "printing = sysv", "printing = hpux" and "printing = aix".
+
+To see what the defaults are for the other print commands when using
+these three options use the "testparm" program.
+
+
+.SS printcap name (G)
+This parameter may be used to override the compiled-in default printcap
+name used by the server (usually /etc/printcap). See the discussion of the
+[printers] section above for reasons why you might want to do this.
+
+For those of you without a printcap (say on SysV) you can just create a
+minimal file that looks like a printcap and set "printcap name =" in
+[global] to point at it.
+
+A minimal printcap file would look something like this:
+
+print1|My Printer 1
+print2|My Printer 2
+print3|My Printer 3
+print4|My Printer 4
+print5|My Printer 5
+
+where the | separates aliases of a printer. The fact that the second
+alias has a space in it gives a hint to Samba that it's a comment.
+
+NOTE: Under AIX the default printcap name is "/etc/qconfig". Samba
+will assume the file is in AIX "qconfig" format if the string
+"/qconfig" appears in the printcap filename.
+
+.B Default:
+       printcap name = /etc/printcap
+
+.B Example:
+       printcap name = /etc/myprintcap
+.SS printer (S)
+A synonym for this parameter is 'printer name'.
+
+This parameter specifies the name of the printer to which print jobs spooled
+through a printable service will be sent.
+
+If specified in the [global] section, the printer name given will be used
+for any printable service that does not have its own printer name specified.
+
+.B Default:
+       none (but may be 'lp' on many systems)
+
+.B Example:
+       printer name = laserwriter
+.SS printer name (S)
+See
+.B printer.
+.SS protocol (G)
+The value of the parameter (a string) is the highest protocol level that will
+be supported by the server. 
+
+Possible values are CORE, COREPLUS, LANMAN1, LANMAN2 and NT1. The relative
+merits of each are discussed in the README file.
+
+.B Default:
+       protocol = NT1
+
+.B Example:
+       protocol = LANMAN1
+.SS public (S)
+A synonym for this parameter is 'guest ok'.
+
+If this parameter is 'yes' for a service, then no password is required
+to connect to the service. Privileges will be those of the guest
+account.
+
+See the section below on user/password validation for more information about
+this option.
+
+.B Default:
+       public = no
+
+.B Example:
+       public = yes
+.SS read list (S)
+This is a list of users that are given read-only access to a
+service. If the connecting user is in this list then they will
+not be given write access, no matter what the "read only" option
+is set to. The list can include group names using the @group syntax.
+
+See also the "write list" option
+
+.B Default:
+     read list =
+
+.B Example:
+     read list = mary, @students
+
+.SS read only (S)
+See
+.B writable
+and
+.B write ok.
+Note that this is an inverted synonym for writable and write ok.
+.SS read prediction (G)
+This options enables or disables the read prediction code used to
+speed up reads from the server. When enabled the server will try to
+pre-read data from the last accessed file that was opened read-only
+while waiting for packets.
+
+.SS Default:
+       read prediction = False
+
+.SS Example:
+       read prediction = True
+.SS read raw (G)
+This parameter controls whether or not the server will support raw reads when
+transferring data to clients.
+
+If enabled, raw reads allow reads of 65535 bytes in one packet. This
+typically provides a major performance benefit.
+
+However, some clients either negotiate the allowable block size incorrectly
+or are incapable of supporting larger block sizes, and for these clients you
+may need to disable raw reads.
+
+In general this parameter should be viewed as a system tuning tool and left
+severely alone. See also
+.B write raw.
+
+.B Default:
+       read raw = yes
+
+.B Example:
+       read raw = no
+.SS read size (G)
+
+The option "read size" affects the overlap of disk reads/writes with
+network reads/writes. If the amount of data being transferred in
+several of the SMB commands (currently SMBwrite, SMBwriteX and
+SMBreadbraw) is larger than this value then the server begins writing
+the data before it has received the whole packet from the network, or
+in the case of SMBreadbraw, it begins writing to the network before
+all the data has been read from disk.
+
+This overlapping works best when the speeds of disk and network access
+are similar, having very little effect when the speed of one is much
+greater than the other.
+
+The default value is 2048, but very little experimentation has been
+done yet to determine the optimal value, and it is likely that the best
+value will vary greatly between systems anyway. A value over 65536 is
+pointless and will cause you to allocate memory unnecessarily.
+
+.B Default:
+       read size = 2048
+
+.B Example:
+       read size = 8192
+
+.SS revalidate (S)
+
+This options controls whether Samba will allow a previously validated
+username/password pair to be used to attach to a share. Thus if you
+connect to \\\\server\\share1 then to \\\\server\\share2 it won't
+automatically allow the client to request connection to the second
+share as the same username as the first without a password.
+
+If "revalidate" is True then the client will be denied automatic
+access as the same username.
+
+.B Default:
+       revalidate = False
+
+.B Example:
+       revalidate = True
+
+.SS root (G)
+See
+.B root directory.
+.SS root dir (G)
+See
+.B root directory.
+.SS root directory (G)
+Synonyms for this parameter are 'root dir' and 'root'.
+
+The server will chroot() to this directory on startup. This is not 
+strictly necessary for secure operation. Even without it the server
+will deny access to files not in one of the service entries. It may 
+also check for, and deny access to, soft links to other parts of the 
+filesystem, or attempts to use .. in file names to access other 
+directories (depending on the setting of the "wide links" parameter).
+
+Adding a "root dir" entry other than "/" adds an extra level of security, 
+but at a price. It absolutely ensures that no access is given to files not
+in the sub-tree specified in the "root dir" option, *including* some files 
+needed for complete operation of the server. To maintain full operability
+of the server you will need to mirror some system files into the "root dir"
+tree. In particular you will need to mirror /etc/passwd (or a subset of it),
+and any binaries or configuration files needed for printing (if required). 
+The set of files that must be mirrored is operating system dependent.
+
+.B Default:
+       root directory = /
+
+.B Example:
+       root directory = /homes/smb
+.SS security (G)
+This option does affects how clients respond to Samba.
+
+The option sets the "security mode bit" in replies to protocol negotiations
+to turn share level security on or off. Clients decide based on this bit 
+whether (and how) to transfer user and password information to the server.
+
+The default is "security=SHARE", mainly because that was the only
+option at one stage.
+
+The alternatives are "security = user" or "security = server". 
+
+If your PCs use usernames that are the same as their usernames on the
+unix machine then you will want to use "security = user". If you
+mostly use usernames that don't exist on the unix box then use
+"security = share".
+
+There is a bug in WfWg that may affect your decision. When in user
+level security a WfWg client will totally ignore the password you type
+in the "connect drive" dialog box. This makes it very difficult (if
+not impossible) to connect to a Samba service as anyone except the
+user that you are logged into WfWg as.
+
+If you use "security = server" then Samba will try to validate the
+username/password by passing it to another SMB server, such as an NT
+box. If this fails it will revert to "security = USER".
+
+See the "password server" option for more details.
+
+.B Default:
+       security = SHARE
+
+.B Example:
+       security = USER
+.SS server string (G)
+This controls what string will show up in the printer comment box in
+print manager and next to the IPC connection in "net view". It can be
+any string that you wish to show to your users.
+
+Note that it DOES NOT affect the string that appears in browse
+lists. That is controlled by a nmbd command line option instead.
+
+A %v will be replaced with the Samba version number.
+
+A %h will be replaced with the hostname.
+
+.B Default:
+       server string = Samba %v
+
+.B Example:
+       server string = University of GNUs Samba Server
+
+.SS smbrun (G)
+This sets the full path to the smbrun binary. This defaults to the
+value in the Makefile.
+
+You must get this path right for many services to work correctly.
+
+.B Default: taken from Makefile
+
+.B Example:
+       smbrun = /usr/local/samba/bin/smbrun
+
+.SS short preserve case (S)
+
+This controls if new short filenames are created with the case that
+the client passes, or if they are forced to be the "default" case.
+
+.B Default:
+       short preserve case = no
+
+See the section on "NAME MANGLING" for a fuller discussion.
+
+.SS root preexec (S)
+
+This is the same as preexec except that the command is run as
+root. This is useful for mounting filesystems (such as cdroms) before
+a connection is finalised.
+
+.SS root postexec (S)
+
+This is the same as postexec except that the command is run as
+root. This is useful for unmounting filesystems (such as cdroms) after
+a connection is closed.
+
+.SS set directory (S)
+If 'set directory = no', then users of the service may not use the setdir
+command to change directory.
+
+The setdir comand is only implemented in the Digital Pathworks client. See the
+Pathworks documentation for details.
+.B Default:
+       set directory = no
+
+.B Example:
+       set directory = yes
+
+.SS share modes (S)
+
+This enables or disables the honouring of the "share modes" during a
+file open. These modes are used by clients to gain exclusive read or
+write access to a file. 
+
+These open modes are not directly supported by unix, so they are
+simulated using lock files in the "lock directory". The "lock
+directory" specified in smb.conf must be readable by all users.
+
+The share modes that are enabled by this option are DENY_DOS,
+DENY_ALL, DENY_READ, DENY_WRITE, DENY_NONE and DENY_FCB.
+
+Enabling this option gives full share compatability but may cost a bit
+of processing time on the unix server. They are enabled by default.
+
+.B Default:
+       share modes = yes
+
+.B Example:
+       share modes = no
+
+.SS socket options (G)
+This option (which can also be invoked with the -O command line
+option) allows you to set socket options to be used when talking with
+the client.
+
+Socket options are controls on the networking layer of the operating
+systems which allow the connection to be tuned.
+
+This option will typically be used to tune your Samba server for
+optimal performance for your local network. There is no way that Samba
+can know what the optimal parameters are for your net, so you must
+experiment and choose them yourself. I strongly suggest you read the
+appropriate documentation for your operating system first (perhaps
+"man setsockopt" will help).
+
+You may find that on some systems Samba will say "Unknown socket
+option" when you supply an option. This means you either mis-typed it
+or you need to add an include file to includes.h for your OS. If the
+latter is the case please send the patch to me
+(samba-bugs@anu.edu.au).
+
+Any of the supported socket options may be combined in any way you
+like, as long as your OS allows it.
+
+This is the list of socket options currently settable using this
+option:
+
+  SO_KEEPALIVE
+
+  SO_REUSEADDR
+
+  SO_BROADCAST
+
+  TCP_NODELAY
+
+  IPTOS_LOWDELAY
+
+  IPTOS_THROUGHPUT
+
+  SO_SNDBUF *
+
+  SO_RCVBUF *
+
+  SO_SNDLOWAT *
+
+  SO_RCVLOWAT *
+
+Those marked with a * take an integer argument. The others can
+optionally take a 1 or 0 argument to enable or disable the option, by
+default they will be enabled if you don't specify 1 or 0.
+
+To specify an argument use the syntax SOME_OPTION=VALUE for example
+SO_SNDBUF=8192. Note that you must not have any spaces before or after
+the = sign.
+
+If you are on a local network then a sensible option might be
+
+socket options = IPTOS_LOWDELAY
+
+If you have an almost unloaded local network and you don't mind a lot
+of extra CPU usage in the server then you could try
+
+socket options = IPTOS_LOWDELAY TCP_NODELAY
+
+If you are on a wide area network then perhaps try setting
+IPTOS_THROUGHPUT. 
+
+Note that several of the options may cause your Samba server to fail
+completely. Use these options with caution!
+
+.B Default:
+       no socket options
+
+.B Example:
+       socket options = IPTOS_LOWDELAY 
+
+
+
+
+.SS status (G)
+This enables or disables logging of connections to a status file that
+smbstatus can read.
+
+With this disabled smbstatus won't be able to tell you what
+connections are active.
+
+.B Default:
+       status = yes
+
+.B Example:
+       status = no
+
+.SS strip dot (G)
+This is a boolean that controls whether to strup trailing dots off
+filenames. This helps with some CDROMs that have filenames ending in a
+single dot.
+
+NOTE: This option is now obsolete, and may be removed in future. You
+should use the "mangled map" option instead as it is much more
+general. 
+
+.SS strict locking (S)
+This is a boolean that controls the handling of file locking in the
+server. When this is set to yes the server will check every read and
+write access for file locks, and deny access if locks exist. This can
+be slow on some systems.
+
+When strict locking is "no" the server does file lock checks only when
+the client explicitly asks for them. 
+
+Well behaved clients always ask for lock checks when it is important,
+so in the vast majority of cases "strict locking = no" is preferable.
+
+.B Default:
+       strict locking = no
+
+.B Example:
+       strict locking = yes
+
+.SS sync always (S)
+
+This is a boolean parameter that controls whether writes will always
+be written to stable storage before the write call returns. If this is
+false then the server will be guided by the clients request in each
+write call (clients can set a bit indicating that a particular write
+should be synchronous). If this is true then every write will be
+followed by a fsync() call to ensure the data is written to disk.
+
+.B Default:
+       sync always = no
+
+.B Example:
+       sync always = yes
+
+.SS time offset (G)
+This parameter is a setting in minutes to add to the normal GMT to
+local time conversion. This is useful if you are serving a lot of PCs
+that have incorrect daylight saving time handling.
+
+.B Default:
+       time offset = 0
+
+.B Example:
+       time offset = 60
+
+.SS user (S)
+See
+.B username.
+.SS username (S)
+A synonym for this parameter is 'user'.
+
+Multiple users may be specified in a comma-delimited list, in which case the
+supplied password will be tested against each username in turn (left to right).
+
+The username= line is needed only when the PC is unable to supply it's own
+username. This is the case for the coreplus protocol or where your
+users have different WfWg usernames to unix usernames. In both these
+cases you may also be better using the \\\\server\\share%user syntax
+instead. 
+
+The username= line is not a great solution in many cases as it means Samba
+will try to validate the supplied password against each of the
+usernames in the username= line in turn. This is slow and a bad idea for
+lots of users in case of duplicate passwords. You may get timeouts or
+security breaches using this parameter unwisely.
+
+Samba relies on the underlying unix security. This parameter does not
+restrict who can login, it just offers hints to the Samba server as to
+what usernames might correspond to the supplied password. Users can
+login as whoever they please and they will be able to do no more
+damage than if they started a telnet session. The daemon runs as the
+user that they log in as, so they cannot do anything that user cannot
+do.
+
+To restrict a service to a particular set of users you can use the
+"valid users=" line.
+
+If any of the usernames begin with a @ then the name will be looked up
+in the groups file and will expand to a list of all users in the group
+of that name. Note that searching though a groups file can take quite
+some time, and some clients may time out during the search.
+
+See the section below on username/password validation for more information
+on how this parameter determines access to the services.
+
+.B Default:
+       The guest account if a guest service, else the name of the service.
+
+.B Examples:
+       username = fred
+       username = fred, mary, jack, jane, @users, @pcgroup
+
+.SS username map (G)
+
+This option allows you to to specify a file containing a mapping of
+usernames from the clients to the server. This can be used for several
+purposes. The most common is to map usernames that users use on dos or
+windows machines to those that the unix box uses. The other is to map
+multiple users to a single username so that they can more easily share
+files.
+
+The map file is parsed line by line. Each line should contain a single
+unix username on the left then a '=' followed by a list of usernames
+on the right. The list of usernames on the right may contain names of
+the form @group in which case they will match any unix username in
+that group. The special client name '*' is a wildcard and matches any
+name.
+
+The file is processed on each line by taking the supplied username and
+comparing it with each username on the right hand side of the '='
+signs. If the supplied name matrches any of the names on the right
+hand side then it is replaced with the name on the left. Processing
+then continues with the next line.
+
+If any line begins with a '#' or a ';' then it is ignored
+
+For example to map from he name "admin" or "administrator" to the unix
+name "root" you would use
+
+       root = admin administrator
+
+Or to map anyone in the unix group "system" to the unix name "sys" you
+would use
+
+       sys = @system
+
+You can have as many mappings as you like in a username map file.
+
+Note that the remapping is applied to all occurances of
+usernames. Thus if you connect to "\\\\server\\fred" and "fred" is
+remapped to "mary" then you will actually be connecting to
+"\\\\server\\mary" and will need to supply a password suitable for
+"mary" not "fred". The only exception to this is the username passwed
+to the "password server" (if you have one). The password server will
+receive whatever username the client supplies without modification.
+
+Also note that no reverse mapping is done. The main effect this has is
+with printing. Users who have been mapped may have trouble deleting
+print jobs as PrintManager under WfWg will think they don't own the
+print job.
+
+.B Default
+       no username map
+
+.B Example
+       username map = /usr/local/samba/lib/users.map
+
+.SS valid chars (S)
+
+The option allows you to specify additional characters that should be
+considered valid by the server in filenames. This is particularly
+useful for national character sets, such as adding u-umlaut or a-ring.
+
+The option takes a list of characters in either integer or character
+form with spaces between them. If you give two characters with a colon
+between them then it will be taken as an lowercase:uppercase pair.
+
+If you have an editor capable of entering the characters into the
+config file then it is probably easiest to use this method. Otherwise
+you can specify the characters in octal, decimal or hexidecimal form
+using the usual C notation.
+
+For example to add the single character 'Z' to the charset (which is a
+pointless thing to do as it's already there) you could do one of the
+following
+
+valid chars = Z
+valid chars = z:Z
+valid chars = 0132:0172
+
+The last two examples above actually add two characters, and alters
+the uppercase and lowercase mappings appropriately.
+
+.B Default
+       Samba defaults to using a reasonable set of valid characters
+       for english systems
+
+.B Example
+        valid chars = 0345:0305 0366:0326 0344:0304
+
+The above example allows filenames to have the swedish characters in
+them. 
+
+.SS valid users (S)
+This is a list of users that should be allowed to login to this
+service. A name starting with @ is interpreted as a unix group.
+
+If this is empty (the default) then any user can login. If a username
+is in both this list and the "invalid users" list then access is
+denied for that user.
+
+The current servicename is substituted for %S. This is useful in the
+[homes] section.
+
+See also "invalid users"
+
+.B Default
+       No valid users list. (anyone can login)
+
+.B Example
+       valid users = greg, @pcusers
+
+.SS volume (S)
+This allows you to override the volume label returned for a
+share. Useful for CDROMs whos installation programs insist on a
+particular volume label.
+
+The default is the name of the share
+
+.SS wide links (S)
+This parameter controls whether or not links in the Unix file system may be
+followed by the server. Links that point to areas within the directory tree
+exported by the server are always allowed; this parameter controls access 
+only to areas that are outside the directory tree being exported.
+
+.B Default:
+       wide links = yes
+
+.B Example:
+       wide links = no
+
+.SS workgroup (G)
+
+This controls what workgroup your server will appear to be in when
+queried by clients. This can be different to the workgroup specified
+in the nmbd configuration, but it is probably best if you set them to
+the same value.
+
+.B Default:
+       set in the Makefile
+
+.B Example:
+       workgroup = MYGROUP
+
+.SS write ok (S)
+See
+.B writable
+and
+.B read only.
+.SS writable (S)
+A synonym for this parameter is 'write ok'. An inverted synonym is 'read only'.
+
+If this parameter is 'no', then users of a service may not create or modify
+files in the service's directory.
+
+Note that a printable service ('printable = yes') will ALWAYS allow 
+writing to the directory (user privileges permitting), but only via
+spooling operations.
+
+.B Default:
+       writable = no
+
+.B Examples:
+       read only = no
+       writable = yes
+       write ok = yes
+.SS write list (S)
+This is a list of users that are given read-write access to a
+service. If the connecting user is in this list then they will be
+given write access, no matter what the "read only" option is set
+to. The list can include group names using the @group syntax.
+
+Note that if a user is in both the read list and the write list then
+they will be given write access.
+
+See also the "read list" option
+
+.B Default:
+     write list =
+
+.B Example:
+     write list = admin, root, @staff
+
+.SS write raw (G)
+This parameter controls whether or not the server will support raw writes when
+transferring data from clients.
+
+.B Default:
+       write raw = yes
+
+.B Example:
+       write raw = no
+.SH NOTE ABOUT USERNAME/PASSWORD VALIDATION
+There are a number of ways in which a user can connect to a
+service. The server follows the following steps in determining if it
+will allow a connection to a specified service. If all the steps fail
+then the connection request is rejected. If one of the steps pass then
+the following steps are not checked.
+
+If the service is marked "guest only = yes" then steps 1 to 5 are skipped
+
+Step 1: If the client has passed a username/password pair and that
+username/password pair is validated by the unix systems password
+programs then the connection is made as that username. Note that this
+includes the \\\\server\\service%username method of passing a username.
+
+Step 2: If the client has previously registered a username with the
+system and now supplies a correct password for that username then the
+connection is allowed.
+
+Step 3: The clients netbios name and any previously used user names
+are checked against the supplied password, if they match then the
+connection is allowed as the corresponding user.
+
+Step 4: If the client has previously validated a username/password
+pair with the server and the client has passed the validation token
+then that username is used. This step is skipped if "revalidate = yes" 
+for this service.
+
+Step 5: If a "user = " field is given in the smb.conf file for the
+service and the client has supplied a password, and that password
+matches (according to the unix systems password checking) with one of
+the usernames from the user= field then the connection is made as the
+username in the "user=" line. If one of the username in the user= list
+begins with a @ then that name expands to a list of names in the group
+of the same name.
+
+Step 6: If the service is a guest service then a connection is made as
+the username given in the "guest account =" for the service,
+irrespective of the supplied password.
+
+
+.SH WARNINGS
+Although the configuration file permits service names to contain spaces, 
+your client software may not. Spaces will be ignored in comparisons anyway,
+so it shouldn't be a problem - but be aware of the possibility.
+
+On a similar note, many clients - especially DOS clients - limit service
+names to eight characters. Smbd has no such limitation, but attempts
+to connect from such clients will fail if they truncate the service names.
+For this reason you should probably keep your service names down to eight 
+characters in length.
+
+Use of the [homes] and [printers] special sections make life for an 
+administrator easy, but the various combinations of default attributes can be
+tricky. Take extreme care when designing these sections. In particular,
+ensure that the permissions on spool directories are correct.
+.SH VERSION
+This man page is (mostly) correct for version 1.9.00 of the Samba suite, plus some
+of the recent patches to it. These notes will necessarily lag behind 
+development of the software, so it is possible that your version of 
+the server has extensions or parameter semantics that differ from or are not 
+covered by this man page. Please notify these to the address below for 
+rectification.
+
+Prior to version 1.5.21 of the Samba suite, the configuration file was
+radically different (more primitive). If you are using a version earlier than
+1.8.05, it is STRONGLY recommended that you upgrade.
+.SH OPTIONS
+Not applicable.
+
+.SH FILES
+Not applicable.
+
+.SH ENVIRONMENT VARIABLES
+Not applicable.
+
+.SH SEE ALSO
+.B smbd(8),
+.B smbclient(1),
+.B nmbd(8),
+.B testparm(1), 
+.B testprns(1),
+.B lpq(1),
+.B hosts_access(5)
+.SH DIAGNOSTICS
+[This section under construction]
+
+Most diagnostics issued by the server are logged in a specified log file. The
+log file name is specified at compile time, but may be overridden on the
+smbd (see smbd(8)) command line.
+
+The number and nature of diagnostics available depends on the debug level used
+by the server. If you have problems, set the debug level to 3 and peruse the
+log files.
+
+Most messages are reasonably self-explanatory. Unfortunately, at time of
+creation of this man page the source code is still too fluid to warrant
+describing each and every diagnostic. At this stage your best bet is still
+to grep the source code and inspect the conditions that gave rise to the 
+diagnostics you are seeing.
+
+.SH BUGS
+None known.
+
+Please send bug reports, comments and so on to:
+
+.RS 3
+.B samba-bugs@anu.edu.au (Andrew Tridgell)
+
+.RS 3
+or to the mailing list
+.RE
+
+.B samba@listproc.anu.edu.au
+
+.RE
+You may also like to subscribe to the announcement channel
+
+.RS 3
+samba-announce@listproc.anu.edu.au
+.RE
+
+To subscribe to these lists send a message to
+listproc@listproc.anu.edu.au with a body of "subscribe samba Your
+Name" or "subscribe samba-announce Your Name".
+
+Errors or suggestions for improvements to the Samba man pages should be 
+mailed to:
+
+.RS 3
+.B samba-bugs@anu.edu.au (Andrew Tridgell)
+.RE
+
diff --git a/docs/manpages/smbclient.1 b/docs/manpages/smbclient.1
new file mode 100644 (file)
index 0000000..5590e01
--- /dev/null
@@ -0,0 +1,1133 @@
+.TH SMBCLIENT 1 17/1/1995 smbclient smbclient
+.SH NAME
+smbclient \- ftp-like Lan Manager client program
+.SH SYNOPSIS
+.B smbclient
+.B servicename
+[
+.B password
+] [
+.B -A
+] [
+.B -E
+] [
+.B -L
+.I host
+] [
+.B -M
+.I host
+] [
+.B -I
+.I IP number
+] [
+.B -N
+] [
+.B -P
+] [
+.B -U
+.I username
+] [
+.B -d
+.I debuglevel
+] [
+.B -l
+.I log basename
+] [
+.B -n
+.I netbios name
+] [
+.B -O
+.I socket options
+] [
+.B -p
+.I port number
+.B -T
+.I tar options
+.B -D
+.I initial directory
+]
+.SH DESCRIPTION
+This program is part of the Samba suite.
+
+.B smbclient
+is a client that can 'talk' to a Lan Manager server. It offers
+an interface similar to that of the 
+.B ftp
+program (see
+.B ftp(1)). Operations include things like getting files from the
+server to the local machine, putting files from the local machine to
+the server, retrieving directory information from the server and so on.
+
+.SH OPTIONS
+.B servicename
+.RS 3
+.B servicename
+is the name of the service you want to use on the server. A service
+name takes the form
+.B "\\\\\\\\server\\\\service"
+where
+.B server
+is the netbios name of the Lan Manager server offering the desired service and
+.B service
+is the name of the service offered. Thus to connect to the service "printer" 
+on the Lan Manager server "lanman", you would use the servicename
+
+.RS 10
+.B "\\\\\\\\lanman\\\\printer"
+.RE
+
+Note that the server name required is NOT necessarily the host name of the
+server! The name required is a Lan Manager server name, which may or may not
+be the same as the hostname of the machine running the server.
+.RE
+
+.B password
+.RS 3
+.B
+password
+is the password required to access the specified service on the
+specified server. If supplied, the
+.B -N
+option (suppress password prompt) is assumed.
+
+There is no default password. If no password is supplied on the command line
+(either here or using the 
+.B -U
+option (see below)) and 
+.B -N
+is not specified, the client will prompt for a password, even if the desired 
+service does not require one. (If prompted for a password and none is 
+required, simply press ENTER to provide a null password.)
+
+Note: Some servers (including OS/2 and Windows for Workgroups) insist
+on an uppercase password. Lowercase or mixed case passwords may be
+rejected by these servers.
+
+Be cautious about including passwords in scripts.
+.RE
+
+.B -A
+
+.RS 3
+This parameter, if specified, causes the maximum debug level to be selected.
+Be warned that this generates prodigious amounts of debug data. There is also
+a security issue involved, as at the maximum debug level cleartext passwords
+may be written to some log files.
+.RE
+
+.B -L
+
+.RS 3
+This option allows you to look at what services are available on a
+server. You use it as "smbclient -L host" and a list should appear.
+The -I option may be useful if your netbios names don't match your 
+tcp/ip host names or if you are trying to reach a host on another
+network. For example:
+
+smbclient -L ftp -I ftp.microsoft.com
+
+will list the shares available on microsofts public server.
+.RE
+
+.B -M
+
+.RS 3
+This options allows you to send messages, using the "WinPopup"
+protocol, to another computer. Once a connection is established you
+then type your message, pressing ^D (control-D) to end.
+
+If the receiving computer is running WinPopup the user will receive
+the message and probably a beep. If they are not running WinPopup the
+message will be lost, and no error message will occur.
+
+The message is also automatically truncated if the message is over
+1600 bytes, as this is the limit of the protocol.
+
+One useful trick is to cat the message through smbclient. For example:
+
+cat mymessage.txt | smbclient -M FRED
+
+will send the message in the file "mymessage.txt" to the machine FRED.
+
+You may also find the -U and -I options useful, as they allow you to
+control the FROM and TO parts of the message. 
+
+Samba currently has no way of receiving WinPopup messages.
+
+Note: Copy WinPopup into the startup group on your WfWg PCs if you
+want them to always be able to receive messages.
+.RE
+
+.B -E
+
+.RS 3
+This parameter, if specified, causes the client to write messages to the
+standard error stream (stderr) rather than to the standard output stream.
+
+By default, the client writes messages to standard output - typically the
+user's tty.
+.RE
+
+.B -I
+.I IP number
+
+.RS 3
+.I IP number
+represents the IP number of the server to connect to. It should
+be specified in standard "a.b.c.d" notation.
+
+Normally the client will attempt to locate the specified Lan Manager server
+by looking it up - that is, broadcasting a request for the given server to
+identify itself. Using this parameter will force the client to assume that
+the server is on the machine with the specified IP number.
+
+There is no default for this parameter. If not supplied, it will be determined
+automatically by the client as described above.
+.RE
+
+.B -N
+
+.RS 3
+If specified, this parameter suppresses the normal password prompt from the
+client to the user. This is useful when accessing a service that does not
+require a password.
+
+Unless a password is specified on the command line or this parameter is
+specified, the client will request a password.
+.RE
+
+.B -O
+.I socket options
+.RS 3
+
+See the socket options section of smb.conf(5) for details
+
+.RE
+.B -P
+
+.RS 3
+If specified, the service requested will be connected to as a printer service
+rather than as a normal filespace service. Operations such as put and get
+will not be applicable for such a connection.
+
+By default, services will be connected to as NON-printer services.
+.RE
+
+.B -U
+.I username
+
+.RS 3
+.I username
+is the user name that will be used by the client to make a connection,
+assuming your server is running a protocol that allows for usernames.
+
+Some servers are fussy about the case of this name, and some insist
+that it must be a valid netbios name.
+
+If no 
+.I username
+is supplied, it will default to an uppercase version of the 
+environment variable 
+.B USER
+or
+.B LOGNAME
+in that order.
+If no 
+.I username
+is supplied and neither environment variable exists the user name will
+be empty.
+
+If the service you are connecting to requires a password, it can be supplied
+using the
+.B -U
+option, by appending a percent symbol ("%") then the password to 
+.I username.
+For example, to attach to a service as user "fred" with password "secret", you
+would specify
+.B -U
+.I fred%secret
+on the command line. Note that there are no spaces around the percent symbol.
+
+If you specify the password as part of
+.I username
+then the 
+.B -N
+option (suppress password prompt) is assumed.
+
+If you specify the password as a parameter AND as part of
+.I username
+then the password as part of
+.I username
+will take precedence. Putting nothing before or nothing after the percent 
+symbol will cause an empty username or an empty password to be used,
+respectively.
+
+Note: Some servers (including OS/2 and Windows for Workgroups) insist
+on an uppercase password. Lowercase or mixed case passwords may be
+rejected by these servers.
+
+Be cautious about including passwords in scripts.
+.RE
+
+.B -d
+.I debuglevel
+.RS 3
+
+debuglevel is an integer from 0 to 5.
+
+The default value if this parameter is not specified is zero.
+
+The higher this value, the more detail will be logged to the log files about
+the activities of the client. At level 0, only critical errors and serious 
+warnings will be logged. Level 1 is a reasonable level for day to day running
+- it generates a small amount of information about operations carried out.
+
+Levels above 1 will generate considerable amounts of log data, and should 
+only be used when investigating a problem. Levels above 3 are designed for 
+use only by developers and generate HUGE amounts of log data, most of which 
+is extremely cryptic.
+.RE
+
+.B -l
+.I log basename
+
+.RS 3
+If specified,
+.I log basename
+specifies a base filename into which operational data from the running client
+will be logged.
+
+The default base name is specified at compile time.
+
+The base name is used to generate actual log file names. For example, if the
+name specified was "log", the following files would be used for log data:
+
+.RS 3
+log.client.debug (containing debugging information)
+
+log.client.in (containing inbound transaction data)
+
+log.client.out (containing outbound transaction data)
+.RE
+
+The log files generated are never removed by the client.
+.RE
+.RE
+
+.B -n
+.I netbios name
+
+.RS 3
+By default, the client will use the local machine's hostname (in
+uppercase) as its netbios name. This parameter allows you to override
+the host name and use whatever netbios name you wish.
+.RE
+
+.B -p
+.I port number
+.RS 3
+
+port number is a positive integer value.
+
+The default value if this parameter is not specified is 139.
+
+This number is the port number that will be used when making connections to
+the server. The standard (well-known) port number for the server is 139, 
+hence the default.
+
+This parameter is not normally specified.
+
+.B -T
+.I tar options
+.RS3 
+
+where tar options are one or more of c,x,I,X,b,g,N or a; used as:
+.LP
+smbclient 
+.B "\\\\\\\\server\\\\share"
+\-TcxIXbgNa
+[
+.IR blocksize
+]
+[
+.IR newer-file
+]
+.IR tarfile
+[
+.IR filenames....
+]
+
+.RS3
+.B c
+Create a tar file on UNIX. Must be followed by the name of a tar file,
+tape device or "-" for standard output. (May be useful to set debugging
+low (-d0)) to avoid corrupting your tar file if using "-"). Mutually
+exclusive with the x flag.
+
+.B x
+Extract (restore) a local tar file back to a share. Unless the -D
+option is given, the tar files will be restored from the top level of
+the share. Must be followed by the name of the tar file, device or "-"
+for standard input. Mutually exclusive with the c flag.
+
+.B I
+Include files and directories. Is the default behaviour when
+.IR filenames
+are specified above. Causes tar files to be included in an extract or create
+(and therefore everything else to be excluded). See example below.
+Filename globbing does not work for included files for extractions (yet).
+
+.B X
+Exclude files and directories. Causes tar files to be excluded from
+an extract or create. See example below.
+Filename globbing does not work for excluded files (yet).
+
+.B b
+Blocksize. Must be followed by a valid (greater than zero) blocksize.
+Causes tar file to be written out in blocksize*TBLOCK (usually 512 byte)
+blocks.
+
+.B g
+Incremental. Only back up files that have the archive bit set. Useful
+only with the c flag.
+
+.B N
+Newer than. Must be followed by the name of a file whose date is
+compared against files found on the share during a create. Only files
+newer than the file specified are backed up to the tar file. Useful
+only with the c flag.
+
+.B a
+Set archive bit. Causes the archive bit to be reset when a file is backed
+up. Useful with the g (and c) flags.
+.LP
+
+.B Examples
+
+smbclient \\\\mypc\\myshare "" -N -Tx backup.tar
+
+Restore from tar file backup.tar into myshare on mypc (no password on share).
+
+smbclient \\\\mypc\\myshare "" -N -TXx backup.tar users/docs
+
+Restore everything except users/docs
+
+smbclient \\\\mypc\\myshare "" -N -Tc backup.tar users/docs
+
+Create a tar file of the files beneath users/docs.
+
+.RE
+
+.B -D
+.I initial directory
+
+.RS3 
+
+Change to initial directory before starting. Probably only of any use
+with the tar (\-T) option.
+
+
+.RE
+
+.SH OPERATIONS
+Once the client is running, the user is presented with a prompt, "smb: \\>".
+The backslash ("\\") indicates the current working directory on the server,
+and will change if the current working directory is changed.
+
+The prompt indicates that the client is ready and waiting to carry out a user
+command. Each command is a single word, optionally followed by parameters
+specific to that command. Command and parameters are space-delimited unless
+these notes specifically state otherwise. All commands are case-insensitive.
+Parameters to commands may or may not be case sensitive, depending on the
+command.
+
+You can specify file names which have spaces in them by quoting the
+name with double quotes, for example "a long file name".
+
+Parameters shown in square brackets (eg., "[parameter]") are optional. If not
+given, the command will use suitable defaults. Parameters shown in angle
+brackets (eg., "<parameter>") are required.
+
+Note that all commands operating on the server are actually performed by
+issuing a request to the server. Thus the behaviour may vary from server to
+server, depending on how the server was implemented.
+
+The commands available are given here in alphabetical order.
+
+.B ?
+.RS 3
+.B Parameters:
+.RS 3
+.I [command]
+
+.RE
+.B Description:
+.RS 3
+If
+.I command
+is specified, the
+.B ?
+command will display a brief informative message about the specified command.
+
+If no command is specified, a list of available commands will be displayed.
+.RE
+.RE
+
+.B !
+.RS 3
+.B Parameters:
+.RS 3
+.I [shell command]
+
+.RE
+.B Description:
+.RS 3
+If
+.I shell command
+is specified, the
+.B !
+command will execute a shell locally and run the specified shell command. If
+no command is specified, a shell will be run.
+.RE
+.RE
+
+.B cd
+.RS 3
+.B Parameters:
+.RS 3
+.I [directory name]
+
+.RE
+.B Description:
+.RS 3
+If
+.I directory name
+is specified, the current working directory
+.B on the server
+will be changed to the directory specified. This operation will fail if for
+any reason the specified directory is inaccessible.
+
+If no directory name is specified, the current working directory
+.B on the server
+will be reported.
+.RE
+.RE
+
+.B del
+.RS 3
+.B Parameters:
+.RS 3
+.I <mask>
+
+.RE
+.B Description:
+.RS 3
+The client will request that the server attempt to delete all files matching
+.I mask
+from the current working directory
+.B on the server.
+.RE
+.RE
+
+.B dir
+.RS 3
+.B Parameters:
+.RS 3
+.I <mask>
+
+.RE
+.B Description:
+.RS 3
+A list of the files matching
+.I mask
+in the current working directory
+.B on the server
+will be retrieved from the server and displayed.
+.RE
+.RE
+
+.B exit
+.RS 3
+.B Parameters:
+.RS 3
+None.
+
+.RE
+.B Description:
+.RS 3
+Terminate the connection with the server and exit from the program.
+.RE
+.RE
+
+.B get
+.RS 3
+.B Parameters:
+.RS 3
+.I <remote file name> [local file name]
+
+.RE
+.B Description:
+.RS 3
+Copy the file called
+.I remote file name
+from the server to the machine running the client. If specified, name the
+local copy
+.I local file name.
+Note that all transfers in smbclient are binary. See also the
+.B lowercase
+command.
+.RE
+.RE
+
+.B help
+.RS 3
+.B Parameters:
+.RS 3
+.I [command]
+
+.RE
+.B Description:
+.RS 3
+See the
+.B ?
+command above.
+.RE
+.RE
+
+.B lcd
+.RS 3
+.B Parameters:
+.RS 3
+.I [directory name]
+
+.RE
+.B Description:
+.RS 3
+If
+.I directory name
+is specified, the current working directory
+.B on the local machine
+will be changed to the directory specified. This operation will fail if for
+any reason the specified directory is inaccessible.
+
+If no directory name is specified, the name of the current working directory
+.B on the local machine
+will be reported.
+.RE
+.RE
+
+.B lowercase
+.RS 3
+.B Parameters:
+.RS 3
+None.
+
+.RE
+.B Description:
+.RS 3
+Toggle lowercasing of filenames for the
+.B get
+and
+.B mget
+commands.
+
+When lowercasing is toggled ON, local filenames are converted to lowercase
+when using the
+.B get
+and
+.B mget
+commands. This is often useful when copying (say) MSDOS files from a server,
+because lowercase filenames are the norm on Unix systems.
+.RE
+.RE
+
+.B ls
+.RS 3
+.B Parameters:
+.RS 3
+.I <mask>
+
+.RE
+.B Description:
+.RS 3
+See the
+.B dir
+command above.
+.RE
+.RE
+
+.B mask
+.RS 3
+.B Parameters:
+.RS 3
+.I <mask>
+
+.RE
+.B Description:
+.RS 3
+This command allows the user to set up a mask which will be used during
+recursive operation of the
+.B mget
+and
+.B mput
+commands.
+
+The masks specified to the
+.B mget
+and
+.B mput
+commands act as filters for directories
+rather than files when recursion is toggled ON.
+
+The mask specified with the
+.B mask
+command is necessary to filter files within those directories. For example,
+if the mask specified in an
+.B mget
+command is "source*"
+.I and
+the mask specified with the
+.B mask
+command is "*.c"
+.I and
+recursion is toggled ON, the
+.B mget
+command will retrieve all files matching "*.c" in all directories below
+and including all directories matching "source*" in the current working 
+directory.
+
+Note that the value for
+.I mask
+defaults to blank (equivalent to "*") and remains so until the
+.B mask
+command is used to change it. It retains the most recently specified value
+indefinitely. To avoid unexpected results it would be wise to change the
+value of
+.I mask
+back to "*" after using the
+.B mget
+or
+.B mput
+commands.
+.RE
+.RE
+
+.B md
+.RS 3
+.B Parameters:
+.RS 3
+.I <directory name>
+
+.RE
+.B Description:
+.RS 3
+See the
+.B mkdir
+command.
+.RE
+.RE
+
+.B mget
+.RS 3
+.B Parameters:
+.RS 3
+.I <mask>
+
+.RE
+.B Description:
+.RS 3
+Copy all files matching
+.I mask
+from the server to the machine running the client.
+
+Note that
+.I mask
+is interpreted differently during recursive operation and non-recursive
+operation - refer to the
+.B recurse
+and
+.B mask
+commands for more information. Note that all transfers in smbclient are 
+binary. See also the
+.B lowercase
+command.
+.RE
+.RE
+
+.B mkdir
+.RS 3
+.B Parameters:
+.RS 3
+.I <directory name>
+
+.RE
+.B Description:
+.RS 3
+Create a new directory 
+.B on the server
+(user access privileges permitting) with the specified name.
+.RE
+.RE
+
+.B mput
+.RS 3
+.B Parameters:
+.RS 3
+.I <mask>
+
+.RE
+.B Description:
+.RS 3
+Copy all files matching
+.I mask
+in the current working directory
+.B on the local machine
+to the current working directory on the server.
+
+Note that
+.I mask
+is interpreted differently during recursive operation and non-recursive
+operation - refer to the
+.B recurse
+and
+.B mask
+commands for more information. Note that all transfers in smbclient are 
+binary.
+.RE
+.RE
+
+.B print
+.RS 3
+.B Parameters:
+.RS 3
+.I <file name>
+
+.RE
+.B Description:
+.RS 3
+Print the specified file
+.B from the local machine
+through a printable service on the server.
+
+See also the
+.B printmode
+command.
+.RE
+.RE
+
+.B printmode
+.RS 3
+.B Parameters:
+.RS 3
+.I <graphics or text>
+
+.RE
+.B Description:
+.RS 3
+Set the print mode to suit either binary data (such as graphical information)
+or text. Subsequent
+.B print
+commands will use the currently set print mode.
+.RE
+.RE
+
+.B prompt
+.RS 3
+.B Parameters:
+.RS 3
+None.
+
+.RE
+.B Description:
+.RS 3
+Toggle prompting for filenames during operation of the
+.B mget
+and
+.B mput
+commands.
+
+When toggled ON, the user will be prompted to confirm the transfer of each
+file during these commands. When toggled OFF, all specified files will be
+transferred without prompting.
+.RE
+.RE
+
+.B put
+.RS 3
+.B Parameters:
+.RS 3
+.I <local file name> [remote file name]
+
+.RE
+.B Description:
+.RS 3
+Copy the file called
+.I local file name
+from the machine running the client to the server. If specified, name the
+remote copy
+.I remote file name.
+Note that all transfers in smbclient are binary. See also the
+.B lowercase
+command.
+.RE
+.RE
+
+.B queue
+.RS 3
+.B Parameters:
+.RS 3
+None.
+
+.RE
+.B Description:
+.RS 3
+Displays the print queue, showing the job id, name, size and current status.
+.RE
+.RE
+
+.B quit
+.RS 3
+.B Parameters:
+.RS 3
+None.
+
+.RE
+.B Description:
+.RS 3
+See the
+.B exit
+command.
+.RE
+.RE
+
+.B rd
+.RS 3
+.B Parameters:
+.RS 3
+.I <directory name>
+
+.RE
+.B Description:
+.RS 3
+See the
+.B rmdir
+command.
+.RE
+.RE
+
+.B recurse
+.RS 3
+.B Parameters:
+.RS 3
+None.
+
+.RE
+.B Description:
+.RS 3
+Toggle directory recursion for the commands
+.B mget
+and
+.B mput
+.
+
+When toggled ON, these commands will process all directories in the source
+directory (ie., the directory they are copying
+.I from
+) and will recurse into any that match the mask specified to the command. Only
+files that match the mask specified using the
+.B mask
+command will be retrieved. See also the
+.mask
+command.
+
+When recursion is toggled OFF, only files from the current working
+directory on the source machine that match the mask specified to the
+.B mget
+or
+.B mput
+commands will be copied, and any mask specified using the
+.B mask
+command will be ignored.
+.RE
+.RE
+
+.B rm
+.RS 3
+.B Parameters:
+.RS 3
+.I <mask>
+
+.RE
+.B Description:
+.RS 3
+Remove all files matching
+.I mask
+from the current working directory
+.B on the server.
+.RE
+.RE
+
+.B rmdir
+.RS 3
+.B Parameters:
+.RS 3
+.I <directory name>
+
+.RE
+.B Description:
+.RS 3
+Remove the specified directory (user access privileges permitting)
+.B from the server.
+.RE
+.RE
+
+.B tar
+.RS 3
+.B Parameters:
+.RS 3
+.I <c|x>[IXbgNa]
+
+.RE
+.B Description:
+.RS 3
+Performs a tar operation - see -T command line option above. Behaviour
+may be affected by the
+.B tarmode
+command (see below). Using the g (incremental) and N (newer) will affect
+tarmode settings. Note that using the "-" option with tar x may not
+work - use the command line option instead.
+.RE
+.RE
+
+.B blocksize
+.RS 3
+.B Parameters
+.RS 3
+.I <blocksize>
+
+.RE
+.B Description
+.RS 3
+Blocksize. Must be followed by a valid (greater than zero) blocksize.
+Causes tar file to be written out in blocksize*TBLOCK (usually 512 byte)
+blocks.
+.RE
+.RE
+
+.B tarmode
+.RS 3
+.B Parameters
+.RS 3
+.I <full|inc|reset|noreset>
+
+.RE
+.B Description
+.RS 3
+Changes tar's behaviour with regard to archive bits. In full mode,
+tar will back up everything regardless of the archive bit setting (this
+is the default mode). In incremental mode, tar will only back up files
+with the archive bit set. In reset mode, tar will reset the archive bit
+on all files it backs up (implies read/write share).
+.RE
+.RE
+
+.B setmode
+.RS 3
+.B Parameters
+.RS 3
+.I <filename> <perm=[+|-]rsha>
+
+.RE
+.B Description
+.RS 3
+A version of the DOS attrib command to set file permissions. For example,
+
+setmode myfile +r
+
+would make myfile read only.
+.RE
+.RE
+
+.SH NOTES
+Some servers are fussy about the case of supplied usernames, passwords, share
+names (aka service names) and machine names. If you fail to connect try
+giving all parameters in uppercase.
+
+It is often necessary to use the
+.B -n
+option when connecting to some types
+of servers. For example OS/2 LanManager insists on a valid netbios name
+being used, so you need to supply a valid name that would be known to
+the server.
+
+.B smbclient
+supports long file names where the server supports the LANMAN2
+protocol.
+
+.SH FILES
+Not applicable.
+
+.SH ENVIRONMENT VARIABLES
+.B USER
+.RS 3
+The variable USER may contain the username of the person using the client.
+This information is used only if the protocol level is high enough to support
+session-level passwords.
+.RE
+
+.SH INSTALLATION
+The location of the client program is a matter for individual system 
+administrators. The following are thus suggestions only.
+
+It is recommended that the client software be installed under the /usr/local
+hierarchy, in a directory readable by all, writeable only by root. The client
+program itself should be executable by all. The client should NOT be setuid 
+or setgid!
+
+The client log files should be put in a directory readable and writable only
+by the user.
+
+To test the client, you will need to know the name of a running Lan manager
+server. It is possible to run the smbd (see
+.B smbd(8)) as an ordinary user - running that server as a daemon on a
+user-accessible port (typically any port number over 1024) would
+provide a suitable test server.
+.SH VERSION
+This man page is (mostly) correct for version 1.9.00 of the Samba suite, plus some
+of the recent patches to it. These notes will necessarily lag behind 
+development of the client software, so it is possible that your version of 
+the client has extensions or parameter semantics that differ from or are not 
+covered by this man page. Please notify these to the address below for 
+rectification.
+.SH SEE ALSO
+.B smbd(8)
+
+.SH DIAGNOSTICS
+[This section under construction]
+
+Most diagnostics issued by the client are logged in a specified log file. The
+log file name is specified at compile time, but may be overridden on the
+command line.
+
+The number and nature of diagnostics available depends on the debug level used
+by the client. If you have problems, set the debug level to 3 and peruse the
+log files.
+
+Most messages are reasonably self-explanatory. Unfortunately, at time of
+creation of this man page the source code is still too fluid to warrant
+describing each and every diagnostic. At this stage your best bet is still
+to grep the source code and inspect the conditions that gave rise to the 
+diagnostics you are seeing.
+
+.SH BUGS
+None known.
+.SH CREDITS
+The original Samba software and related utilities were created by 
+Andrew Tridgell (samba-bugs@anu.edu.au). Andrew is also the Keeper
+of the Source for this project.
+
+This man page written by Karl Auer (Karl.Auer@anu.edu.au)
+
+See
+.B smb.conf(5) for a full list of contributors and details on how to 
+submit bug reports, comments etc.
diff --git a/docs/manpages/smbd.8 b/docs/manpages/smbd.8
new file mode 100644 (file)
index 0000000..bae41b2
--- /dev/null
@@ -0,0 +1,407 @@
+.TH SMBD 8 17/1/1995 smbd smbd
+.SH NAME
+smbd \- provide SMB (aka LanManager) services to clients
+.SH SYNOPSIS
+.B smbd
+[
+.B -D
+] [
+.B -a
+] [
+.B -d
+.I debuglevel
+] [
+.B -l
+.I log file
+] [
+.B -p
+.I port number
+] [
+.B -O
+.I socket options
+] [
+.B -s
+.I configuration file
+]
+.SH DESCRIPTION
+This program is part of the Samba suite.
+
+.B smbd
+is a server that can provide most SMB services. The 
+server provides filespace and printer services to clients using the SMB 
+protocol. This is compatible with the LanManager protocol, and can
+service LanManager clients.
+
+An extensive description of the services that the server can provide is given
+in the man page for the configuration file controlling the attributes of those
+services (see
+.B smb.conf(5)). This man page will not describe the services, but
+will concentrate on the administrative aspects of running the server.
+
+Please note that there are significant security implications to running this
+server, and
+.B smb.conf(5) should be regarded as mandatory reading before proceeding with 
+installation.
+
+A session is created whenever a client requests one. Each client gets a copy
+of the server for each session. This copy then services all connections made
+by the client during that session. When all connections from its client are
+are closed, the copy of the server for that client terminates.
+
+The configuration file is automatically reloaded if it changes. You
+can force a reload by sending a SIGHUP to the server.
+
+.SH OPTIONS
+.B -D
+
+.RS 3
+If specified, this parameter causes the server to operate as a daemon. That is,
+it detaches itself and runs in the background, fielding requests on the 
+appropriate port.
+
+By default, the server will NOT operate as a daemon.
+.RE
+
+.B -a
+
+.RS 3
+If this parameter is specified, the log files will be overwritten with each 
+new connection. By default, the log files will be appended to.
+.RE
+
+.B -d
+.I debuglevel
+.RS 3
+
+debuglevel is an integer from 0 to 5.
+
+The default value if this parameter is not specified is zero.
+
+The higher this value, the more detail will be logged to the log files about
+the activities of the server. At level 0, only critical errors and serious 
+warnings will be logged. Level 1 is a reasonable level for day to day running
+- it generates a small amount of information about operations carried out.
+
+Levels above 1 will generate considerable amounts of log data, and should 
+only be used when investigating a problem. Levels above 3 are designed for 
+use only by developers and generate HUGE amounts of log data, most of which 
+is extremely cryptic.
+.RE
+
+.B -l
+.I log file
+
+.RS 3
+If specified,
+.I logfile
+specifies a base filename into which operational data from the running server
+will be logged.
+
+The default base name is specified at compile time.
+
+The base name is used to generate actual log file names. For example, if the
+name specified was "log", the following files would be used for log data:
+
+.RS 3
+log.debug (containing debugging information)
+
+log.in (containing inbound transaction data)
+
+log.out (containing outbound transaction data)
+.RE
+
+The log files generated are never removed by the server.
+.RE
+
+.B -O
+.I socket options
+.RS 3
+
+See the socket options section of smb.conf(5) for details
+
+.RE
+.B -p
+.I port number
+.RS 3
+
+port number is a positive integer value.
+
+The default value if this parameter is not specified is 139.
+
+This number is the port number that will be used when making connections to
+the server from client software. The standard (well-known) port number for the
+server is 139, hence the default. If you wish to run the server as an ordinary
+user rather than as root, most systems will require you to use a port number
+greater than 1024 - ask your system administrator for help if you are in this
+situation.
+
+This parameter is not normally specified except in the above situation.
+.RE
+
+.B -s
+.I configuration file
+
+.RS 3
+The default configuration file name is determined at compile time.
+
+The file specified contains the configuration details required by the server.
+The information in this file includes server-specific information such as
+what printcap file to use, as well as descriptions of all the services that the
+server is to provide. See
+.B smb.conf(5) for more information.
+.RE
+
+.SH FILES
+
+.B /etc/inetd.conf
+
+.RS 3
+If the server is to be run by the inetd meta-daemon, this file must contain
+suitable startup information for the meta-daemon. See the section 
+"INSTALLATION" below.
+.RE
+
+.B /etc/rc
+
+.RS 3
+(or whatever initialisation script your system uses)
+
+If running the server as a daemon at startup, this file will need to contain
+an appropriate startup sequence for the server. See the section "INSTALLATION"
+below.
+.RE
+
+.B /etc/services
+
+.RS 3
+If running the server via the meta-daemon inetd, this file must contain a
+mapping of service name (eg., netbios-ssn)  to service port (eg., 139) and
+protocol type (eg., tcp). See the section "INSTALLATION" below.
+.RE
+
+.B /usr/local/smb/smb.conf
+
+.RS 3
+This file describes all the services the server is to make available to
+clients. See
+.B smb.conf(5) for more information.
+.RE
+.RE
+
+.SH LIMITATIONS
+
+On some systems smbd cannot change uid back to root after a setuid() call.
+Such systems are called "trapdoor" uid systems. If you have such a system,
+you will be unable to connect from a client (such as a PC) as two different
+users at once. Attempts to connect the second user will result in "access
+denied" or similar.
+
+.SH ENVIRONMENT VARIABLES
+
+.B PRINTER
+
+.RS 3
+If no printer name is specified to printable services, most systems will
+use the value of this variable (or "lp" if this variable is not defined)
+as the name of the printer to use. This is not specific to the server,
+however.
+.RE
+
+.SH INSTALLATION
+The location of the server and its support files is a matter for individual
+system administrators. The following are thus suggestions only.
+
+It is recommended that the server software be installed under the
+/usr/local hierarchy, in a directory readable by all, writeable only
+by root. The server program itself should be executable by all, as
+users may wish to run the server themselves (in which case it will of
+course run with their privileges).  The server should NOT be
+setuid. On some systems it may be worthwhile to make smbd setgid to an
+empty group. This is because some systems may have a security hole where
+daemon processes that become a user can be attached to with a
+debugger. Making the smbd file setgid to an empty group may prevent
+this hole from being exploited. This secrity hole and the suggested
+fix has only been confirmed on Linux at the time this was written. It
+is possible that this hole only exists in Linux, as testing on other
+systems has thus far shown them to be immune.
+
+The server log files should be put in a directory readable and writable only
+by root, as the log files may contain sensitive information.
+
+The configuration file should be placed in a directory readable and writable
+only by root, as the configuration file controls security for the services
+offered by the server. The configuration file can be made readable by all if
+desired, but this is not necessary for correct operation of the server and
+is not recommended. A sample configuration file "smb.conf.sample" is supplied
+with the source to the server - this may be renamed to "smb.conf" and 
+modified to suit your needs.
+
+The remaining notes will assume the following:
+
+.RS 3
+smbd (the server program) installed in /usr/local/smb
+
+smb.conf (the configuration file) installed in /usr/local/smb
+
+log files stored in /var/adm/smblogs
+.RE
+
+The server may be run either as a daemon by users or at startup, or it may
+be run from a meta-daemon such as inetd upon request. If run as a daemon, the
+server will always be ready, so starting sessions will be faster. If run from 
+a meta-daemon some memory will be saved and utilities such as the tcpd 
+TCP-wrapper may be used for extra security.
+
+When you've decided, continue with either "RUNNING THE SERVER AS A DAEMON" or
+"RUNNING THE SERVER ON REQUEST".
+.SH RUNNING THE SERVER AS A DAEMON
+To run the server as a daemon from the command line, simply put the "-D" option
+on the command line. There is no need to place an ampersand at the end of the
+command line - the "-D" option causes the server to detach itself from the
+tty anyway.
+
+Any user can run the server as a daemon (execute permissions permitting, of 
+course). This is useful for testing purposes, and may even be useful as a
+temporary substitute for something like ftp. When run this way, however, the
+server will only have the privileges of the user who ran it.
+
+To ensure that the server is run as a daemon whenever the machine is started,
+and to ensure that it runs as root so that it can serve multiple clients, you 
+will need to modify the system startup files. Wherever appropriate (for
+example, in /etc/rc), insert the following line, substituting 
+port number, log file location, configuration file location and debug level as
+desired:
+
+.RS 3
+/usr/local/smb/smbd -D -l /var/adm/smblogs/log -s /usr/local/smb/smb.conf
+.RE
+
+(The above should appear in your initialisation script as a single line. 
+Depending on your terminal characteristics, it may not appear that way in
+this man page. If the above appears as more than one line, please treat any 
+newlines or indentation as a single space or TAB character.)
+
+If the options used at compile time are appropriate for your system, all
+parameters except the desired debug level and "-D" may be omitted. See the
+section "OPTIONS" above.
+.SH RUNNING THE SERVER ON REQUEST
+If your system uses a meta-daemon such as inetd, you can arrange to have the
+smbd server started whenever a process attempts to connect to it. This requires
+several changes to the startup files on the host machine. If you are
+experimenting as an ordinary user rather than as root, you will need the 
+assistance of your system administrator to modify the system files.
+
+You will probably want to set up the name server
+.B nmbd
+at the same time as
+the smbd - refer to the man page 
+.B nmbd(8).
+
+First, ensure that a port is configured in the file /etc/services. The 
+well-known port 139 should be used if possible, though any port may be used.
+
+Ensure that a line similar to the following is in /etc/services:
+
+.RS 3
+netbios-ssn    139/tcp
+.RE
+
+Note for NIS/YP users - you may need to rebuild the NIS service maps rather
+than alter your local /etc/services file.
+
+Next, put a suitable line in the file /etc/inetd.conf (in the unlikely event
+that you are using a meta-daemon other than inetd, you are on your own). Note
+that the first item in this line matches the service name in /etc/services.
+Substitute appropriate values for your system in this line (see
+.B inetd(8)):
+
+.RS 3
+netbios-ssn stream tcp nowait root /usr/local/smb/smbd -d1 
+-l/var/adm/smblogs/log -s/usr/local/smb/smb.conf
+.RE
+
+(The above should appear in /etc/inetd.conf as a single line. Depending on 
+your terminal characteristics, it may not appear that way in this man page.
+If the above appears as more than one line, please treat any newlines or 
+indentation as a single space or TAB character.)
+
+Note that there is no need to specify a port number here, even if you are 
+using a non-standard port number.
+
+Lastly, edit the configuration file to provide suitable services. To start
+with, the following two services should be all you need:
+
+.RS 3
+[homes]
+.RS 3
+ writable = yes
+.RE
+
+[printers]
+.RS 3
+ writable = no
+ printable = yes
+ path = /tmp
+ public = yes
+.RE
+.RE
+
+This will allow you to connect to your home directory and print to any printer
+supported by the host (user privileges permitting).
+.SH TESTING THE INSTALLATION
+If running the server as a daemon, execute it before proceeding. If
+using a meta-daemon, either restart the system or kill and restart the 
+meta-daemon. Some versions of inetd will reread their configuration tables if
+they receive a HUP signal.
+
+If your machine's name is "fred" and your name is "mary", you should now be
+able to connect to the service "\\\\fred\\mary".
+
+To properly test and experiment with the server, we recommend using the
+smbclient program (see
+.B smbclient(1)).
+.SH VERSION
+This man page is (mostly) correct for version 1.9.00 of the Samba suite, plus some
+of the recent patches to it. These notes will necessarily lag behind 
+development of the software, so it is possible that your version of 
+the server has extensions or parameter semantics that differ from or are not 
+covered by this man page. Please notify these to the address below for 
+rectification.
+.SH SEE ALSO
+.B hosts_access(5),
+.B inetd(8),
+.B nmbd(8), 
+.B smb.conf(5),
+.B smbclient(1),
+.B testparm(1), 
+.B testprns(1)
+
+.SH DIAGNOSTICS
+[This section under construction]
+
+Most diagnostics issued by the server are logged in a specified log file. The
+log file name is specified at compile time, but may be overridden on the
+command line.
+
+The number and nature of diagnostics available depends on the debug level used
+by the server. If you have problems, set the debug level to 3 and peruse the
+log files.
+
+Most messages are reasonably self-explanatory. Unfortunately, at time of
+creation of this man page the source code is still too fluid to warrant
+describing each and every diagnostic. At this stage your best bet is still
+to grep the source code and inspect the conditions that gave rise to the 
+diagnostics you are seeing.
+
+.SH BUGS
+None known.
+.SH CREDITS
+The original Samba software and related utilities were created by 
+Andrew Tridgell (samba-bugs@anu.edu.au). Andrew is also the Keeper
+of the Source for this project.
+
+This man page written by Karl Auer (Karl.Auer@anu.edu.au)
+
+See
+.B smb.conf(5) for a full list of contributors and details on how to 
+submit bug reports, comments etc.
diff --git a/docs/manpages/smbrun.1 b/docs/manpages/smbrun.1
new file mode 100644 (file)
index 0000000..1608d3b
--- /dev/null
@@ -0,0 +1,70 @@
+.TH SMBRUN 1 17/1/1995 smbrun smbrun
+.SH NAME
+smbrun \- interface program between smbd and external programs
+.SH SYNOPSIS
+.B smbrun
+.I shell-command
+.SH DESCRIPTION
+This program is part of the Samba suite.
+
+.B smbrun
+is a very small 'glue' program, which runs shell commands for
+the
+.B smbd
+daemon (see
+.B smbd(8)).
+
+It first changes to the highest effective user and group ID that it can, 
+then runs the command line provided using the system() call. This program is
+necessary to allow some operating systems to run external programs as non-root.
+.SH OPTIONS
+.I shell-command
+
+.RS 3
+The shell command to execute.
+
+The command should have a fully-qualified path.
+.RE
+.SH ENVIRONMENT VARIABLES
+The PATH variable set for the environment in which
+.B smbrun
+is executed will affect what executables are located and executed if a
+fully-qualified path is not given in the command.
+
+.SH INSTALLATION
+The location of the server and its support files is a matter for individual
+system administrators. The following are thus suggestions only.
+
+It is recommended that the
+.B smbrun
+program be installed under the /usr/local hierarchy, in a directory readable
+by all, writeable only by root. The program should be executable by all.
+The program should NOT be setuid or setgid!
+.SH VERSION
+This man page is (mostly) correct for version 1.9.00 of the Samba suite, plus some
+of the recent patches to it. These notes will necessarily lag behind 
+development of the software, so it is possible that your version of 
+the program has extensions or parameter semantics that differ from or are not 
+covered by this man page. Please notify these to the address below for 
+rectification.
+.SH SEE ALSO
+.B smbd(8), 
+.B smb.conf(8) 
+.SH DIAGNOSTICS
+If smbrun cannot be located or cannot be executed by
+.B smbd
+then appropriate messages will be found in the smbd logs. Other diagnostics are
+dependent on the shell-command being run. It is advisable for your shell
+commands to issue suitable diagnostics to aid trouble-shooting.
+.SH BUGS
+None known.
+.SH CREDITS
+The original Samba software and related utilities were created by 
+Andrew Tridgell (samba-bugs@anu.edu.au). Andrew is also the Keeper
+of the Source for this project.
+
+This man page was written by Karl Auer (Karl.Auer@anu.edu.au)
+
+See
+.B smb.conf(5) for a full list of contributors and details of how to 
+submit bug reports, comments etc.
diff --git a/docs/manpages/smbstatus.1 b/docs/manpages/smbstatus.1
new file mode 100644 (file)
index 0000000..76dc50c
--- /dev/null
@@ -0,0 +1,52 @@
+.TH SMBSTATUS 1 17/1/1995 smbstatus smbstatus
+.SH NAME
+smbstatus \- report on current Samba connections
+.SH SYNOPSIS
+.B smbstatus
+[-d]
+[-s
+.I configuration file
+]
+.SH DESCRIPTION
+This program is part of the Samba suite.
+
+.B smbstatus
+is a very simple program to list the current Samba connections
+
+Just run the program and the output is self explanatory. You can offer
+a configuration filename to override the default. The default is
+CONFIGFILE from the Makefile.
+
+Option
+.I -d
+gives verbose output.
+
+.I -p
+print a list of smbd processes and exit. Useful for scripting.
+
+.SH ENVIRONMENT VARIABLES
+Not applicable.
+
+.SH INSTALLATION
+The location of the server and its support files is a matter for individual
+system administrators. The following are thus suggestions only.
+
+It is recommended that the
+.B smbstatus
+program be installed under the /usr/local hierarchy, in a directory readable
+by all, writeable only by root. The program itself should be executable by all.
+
+.SH VERSION
+This man page is (mostly) correct for version 1.9.00 of the Samba suite, plus some
+of the recent patches to it. These notes will necessarily lag behind 
+development of the software, so it is possible that your version of 
+the program has extensions or parameter semantics that differ from or are not 
+covered by this man page. Please notify these to the address below for 
+rectification.
+.SH SEE ALSO
+.B smb.conf(5),
+.B smbd(8)
+
+See
+.B smb.conf(5) for a full list of contributors and details on how to 
+submit bug reports, comments etc.
diff --git a/docs/manpages/smbtar.1 b/docs/manpages/smbtar.1
new file mode 100644 (file)
index 0000000..0f1c38c
--- /dev/null
@@ -0,0 +1,167 @@
+.TH SMBTAR 1 18/2/96 smbtar smbtar
+.SH NAME
+smbtar \- shell script for backing up SMB shares directly to UNIX tape drive
+.SH SYNOPSIS
+.B smbtar
+.B \-s
+.I server
+.B [ \-p
+.I password
+.B ]
+.B [ \-x
+.I service
+.B ]
+.B [ \-X ]
+.B [ \-d
+.I directory
+.B ]
+.B [ \-u
+.I user
+.B ]
+.B [ \-t
+.I tape
+.B ]
+.B [ \-b
+.I blocksize
+.B ]
+.B [ \-N
+.I filename
+.B ]
+.B [ \-i ]
+.B [ \-r ]
+.B [ \-l ]
+.B [ \-v ]
+.I filenames...
+
+.SH DESCRIPTION
+This program is an extension to the Samba suite.
+
+.B smbtar
+is a very small shell script on top of smbclient, which dumps SMB
+shares directly to tape.
+
+.SH OPTIONS
+.B \-s
+.I server
+.RS 3
+The PC that the share resides upon.
+.RE
+
+.B \-x
+.I service
+.RS 3
+The share name on the PC to connect to. Default:
+.I backup.
+.RE
+
+.B \-X
+.RS 3
+Exclude mode. Exclude
+.I filenames...
+from tar create or restore.
+.RE
+
+.B \-d
+.I directory
+.RS 3
+Change to initial
+.I directory
+before restoring / backing up files.
+.RE
+
+.B \-v
+.RS 3
+Verbose mode.
+.RE
+
+.B \-p
+.I password
+
+.RS 3
+The password to use to access a share. Default: none
+.RE
+
+.B \-u
+.I user
+.RS 3
+The user id to connect as. Default: UNIX login name.
+.RE
+
+.B \-t
+.I tape
+.RS 3
+Tape device. May be regular file or tape device. Default: Tape environmental
+variable; if not set, a file called
+.I tar.out.
+.RE
+
+.B \-b
+.I blocksize
+.RS 3
+Blocking factor. Defaults to 20. See tar(1) for a fuller explanation.
+.RE
+
+.B \-N
+.I filename
+.RS 3
+Backup only files newer than filename. Could be used (for example) on a log
+file to implement incremental backups.
+.RE
+
+.B \-i
+.RS 3
+Incremental mode; tar files are only backed up if they have the
+archive bit set. The archive bit is reset after each file is read.
+.RE
+
+.B \-r
+.RS 3
+Restore. Files are restored to the share from the tar file.
+.RE
+
+.B \-l
+.RS 3
+Debug level. Corresponds to -d flag on smbclient(1).
+.RE
+
+.SH ENVIRONMENT VARIABLES
+The TAPE variable specifies the default tape device to write to. May
+be overidden with the -t option.
+
+.SH BUGS
+The smbtar script has different options from ordinary tar and tar
+called from smbclient.
+
+.SH CAVEATS
+Sites that are more careful about security may not like the way
+the script handles PC passwords. Backup and restore work on entire shares,
+should work on file lists.
+
+.SH VERSION
+This man page is correct for version 1.9.15p8 of the Samba suite.
+
+.SH SEE ALSO
+.B smbclient
+(8), 
+.B smb.conf
+(8) 
+.SH DIAGNOSTICS
+See diagnostics for 
+.B smbclient
+command.
+
+.SH CREDITS
+The original Samba software and related utilities were created by 
+Andrew Tridgell (samba-bugs@anu.edu.au). Andrew is also the Keeper
+of the Source for this project.
+
+Ricky Poulten (poultenr@logica.co.uk) wrote the tar extension and this
+man page. The smbtar script was heavily rewritten and improved by
+Martin Kraemer <Martin.Kraemer@mch.sni.de>. Many thanks to everyone
+who suggested extensions, improvements, bug fixes, etc.
+
+See
+.B smb.conf
+(5) for a full list of contributors and details of how to submit bug reports,
+comments etc.
+
diff --git a/docs/manpages/testparm.1 b/docs/manpages/testparm.1
new file mode 100644 (file)
index 0000000..4a0ffcb
--- /dev/null
@@ -0,0 +1,104 @@
+.TH TESTPARM 1 17/1/1995 testparm testparm
+.SH NAME
+testparm \- check an smbd configuration file for internal correctness
+.SH SYNOPSIS
+.B testparm
+[
+.I configfilename
+[
+.I hostname
+.I hostIP
+]
+]
+.SH DESCRIPTION
+This program is part of the Samba suite.
+
+.B testparm
+is a very simple test program to check an
+.B smbd
+configuration
+file for internal correctness. If this program reports no problems, you can use
+the configuration file with confidence that smbd will successfully
+load the configuration file.
+
+Note that this is NOT a guarantee that the services specified in the
+configuration file will be available or will operate as expected.
+
+If the optional host name and host IP address are specified on the
+command line, this test program will run through the service entries
+reporting whether the specified host has access to each service.
+.SH OPTIONS
+.I configfilename
+
+.RS 3
+This is the name of the configuration file to check.
+.RE
+
+.I hostname
+
+.RS 3
+This is the name of the host to check access on.
+
+If this parameter is supplied, the
+.I hostIP
+parameter must also be supplied, or strange things may happen.
+.RE
+
+.I hostIP
+
+.RS 3
+This is the IP number of the host specified in the previous parameter.
+
+This number must be supplied if the
+.I hostname
+parameter is supplied, or strange things may happen.
+.RE
+.SH FILES
+.B smb.conf
+.RS 3
+This is usually the name of the configuration file used by smbd.
+.RE
+.SH ENVIRONMENT VARIABLES
+Not applicable.
+
+.SH INSTALLATION
+The location of the server and its support files is a matter for individual
+system administrators. The following are thus suggestions only.
+
+It is recommended that the
+.B testparm
+program be installed under the /usr/local hierarchy, in a directory readable
+by all, writeable only by root. The program itself should be executable by all.
+The program should NOT be setuid or setgid!
+.SH VERSION
+This man page is (mostly) correct for version 1.9.00 of the Samba suite, plus some
+of the recent patches to it. These notes will necessarily lag behind 
+development of the software, so it is possible that your version of 
+the program has extensions or parameter semantics that differ from or are not 
+covered by this man page. Please notify these to the address below for 
+rectification.
+.SH SEE ALSO
+.B smb.conf(5),
+.B smbd(8)
+.SH DIAGNOSTICS
+The program will issue a message saying whether the configuration file loaded
+OK or not. This message may be preceded by errors and warnings if the file
+did not load. If the file was loaded OK, the program then dumps all known
+service details to stdout.
+
+If a host name is specified but no host IP number, all bets are off.
+
+Other messages are self-explanatory.
+.SH BUGS
+None known.
+.SH CREDITS
+The original Samba software and related utilities were created by 
+Andrew Tridgell (samba-bugs@anu.edu.au). Andrew is also the Keeper
+of the Source for this project.
+
+The testparm program and this man page were written by Karl Auer
+(Karl.Auer@anu.edu.au)
+
+See
+.B samba(7) for a full list of contributors and details on how to 
+submit bug reports, comments etc.
diff --git a/docs/manpages/testprns.1 b/docs/manpages/testprns.1
new file mode 100644 (file)
index 0000000..f1c3d3e
--- /dev/null
@@ -0,0 +1,107 @@
+.TH TESTPRNS 1 17/1/1995 testprns testprns
+.SH NAME
+testprns \- check printer name for validity with smbd
+.SH SYNOPSIS
+.B testprns
+.I printername
+[
+.I printcapname
+]
+.SH DESCRIPTION
+This program is part of the Samba suite.
+
+.B testprns
+is a very simple test program to determine whether a given 
+printer name is valid for use in a service to be provided by
+.B smbd.
+
+"Valid" in this context means "can be found in the printcap specified". This
+program is very stupid - so stupid in fact that it would be wisest to always
+specify the printcap file to use.
+.SH OPTIONS
+.I printername
+
+.RS 3
+The printer name to validate.
+
+Printer names are taken from the first field in each record in the printcap
+file, single printer names and sets of aliases separated by vertical bars
+("|") are recognised. Note that no validation or checking of the printcap
+syntax is done beyond that required to extract the printer name. It may
+be that the print spooling system is more forgiving or less forgiving
+than 
+.B testprns
+however if
+.B testprns
+finds the printer then smbd should do as well.
+
+.RE
+
+.I printcapname
+
+.RS 3
+This is the name of the printcap file to search for the given printer name
+in.
+
+If no printcap name is specified,
+.B testprns
+will attempt to scan the printcap file specified at compile time 
+(PRINTCAP_NAME).
+.RE
+.SH FILES
+.B /etc/printcap
+.RS 3
+This is usually the default printcap file to scan. See
+.B printcap(5)).
+.RE
+.SH ENVIRONMENT VARIABLES
+Not applicable.
+
+.SH INSTALLATION
+The location of the server and its support files is a matter for individual
+system administrators. The following are thus suggestions only.
+
+It is recommended that the
+.B testprns
+program be installed under the /usr/local hierarchy, in a directory readable
+by all, writeable only by root. The program should be executable by all.
+The program should NOT be setuid or setgid!
+.SH VERSION
+This man page is (mostly) correct for version 1.9.00 of the Samba suite, plus some
+of the recent patches to it. These notes will necessarily lag behind 
+development of the software, so it is possible that your version of 
+the program has extensions or parameter semantics that differ from or are not 
+covered by this man page. Please notify these to the address below for 
+rectification.
+.SH SEE ALSO
+.B printcap(5),
+.B smbd(8), 
+.B smbclient(1)
+.SH DIAGNOSTICS
+If a printer is found to be valid, the message "Printer name <printername> is 
+valid" will be displayed.
+
+If a printer is found to be invalid, the message "Printer name <printername> 
+is not valid" will be displayed.
+
+All messages that would normally be logged during operation of smbd are
+logged by this program to the file
+.I test.log
+in the current directory. The program runs at debuglevel 3, so quite extensive
+logging information is written. The log should be checked carefully for errors
+and warnings.
+
+Other messages are self-explanatory.
+.SH BUGS
+None known.
+.SH CREDITS
+The original Samba software and related utilities were created by 
+Andrew Tridgell (samba-bugs@anu.edu.au). Andrew is also the Keeper
+of the Source for this project.
+
+The testprns program and this man page were written by Karl Auer
+(Karl.Auer@anu.edu.au)
+
+See
+.B samba(7) for a full list of contributors and details of how to 
+submit bug reports, comments etc.
diff --git a/docs/samba.lsm b/docs/samba.lsm
new file mode 100644 (file)
index 0000000..503ba1e
--- /dev/null
@@ -0,0 +1,26 @@
+Begin2
+Title        = Samba
+Version      = 1.8.0
+Desc1        = Samba is a SMB based file and print server for unix. It
+Desc2        = provides access to unix file and print services from
+Desc3        = SMB compatible clients such as WinNT, WfWg, OS/2
+Desc4        = and Pathworks. It also includes a ftp-style unix client
+Desc5        = and a netbios nameserver.
+Author       = Andrew Tridgell
+AuthorEmail  = samba-bugs@anu.edu.au
+Maintainer   = Andrew Tridgell
+MaintEmail   = samba-bugs@anu.edu.au
+Site1        = nimbus.anu.edu.au
+Path1        = pub/tridge/samba/
+File1        = samba-latest.tar.gz
+FileSize1    = 200K
+Required1    = Ansi-C compiler and a TCP/IP network.
+CopyPolicy1  = GNU Public License
+Keywords     = LanManager, SMB, Networking
+Comment1     = To join the Samba mailing list send mail to 
+Comment2     = listproc@listproc.anu.edu.au with a body of
+Comment3     = "subscribe samba Your Name"
+Entered      = October 1994
+EnteredBy    = Andrew Tridgell
+End
+
diff --git a/docs/textdocs/BROWSING.txt b/docs/textdocs/BROWSING.txt
new file mode 100644 (file)
index 0000000..8a09d22
--- /dev/null
@@ -0,0 +1,145 @@
+BROWSING
+========
+
+Samba now fully supports browsing. The browsing is supported by nmbd
+and is also controlled by options in the smb.conf file (see
+smb.conf(5)).
+
+Samba can act as a browse master for a workgroup, but currently cannot
+act as a domain controller. The ability to be a domain controller will
+be added in a later version.
+
+To get browsing to work you need to run nmbd as usual, but will need
+to use the "workgroup" option in smb.conf to control what workgroup
+Samba becomes a part of.
+
+The -G option is most useful for simple setups where Samba is browsable
+in only one workgroup. In more complex cases the lmhosts file is
+better.
+
+Be very careful setting up your lmhosts file. An incorrectly setup
+lmhosts file can have disasterous results for your net!
+
+A simple lmhosts file might be:
+
+# This is a simple lmhosts file
+#
+# This is a host alias. Anyone querying this name
+# will get the specified IP
+192.0.2.17 SMBDATA
+#
+# first put ourselves in workgroup MYGROUP using
+# our own net address
+0.0.0.0 MYGROUP G
+
+Note in the above that I overrode what workgroup Samba is in using the
+G flag. Also note that the 0.0.0.0 address is used, which will be
+automatically replaced with the broadcast address for groups, and with
+the local IP address for other entries.
+
+Samba also has a useful option for a Samba server to offer itself for
+browsing on another subnet.
+
+This works by the lmhosts file specifying a broadcast address on the
+other network to use to find a browse master for the workgroup.
+
+For example if you wanted yourself to appear in the workgroup STAFF on
+the network which has a broadcast of 192.0.3.255 then this entry would
+do the trick:
+
+# put ourselves in the STAFF workgroup on the other subnet
+192.0.3.255 STAFF G
+
+Notice the G at the end! It is very important you include this as this
+entry without the G could cause a broadcast storm! 
+
+If something doesn't work then hopefully the log.nmb file will
+help you track down the problem. Try a debug level of 2 or 3 for
+finding problems.
+
+Note that if it doesn't work for you, then you should still be able to
+type the server name as \\SERVER in filemanager then hit enter and
+filemanager should display the list of available shares.
+
+Some people find browsing fails because they don't have the global
+"guest account" set to a valid account. Remember that the IPC$
+connection that lists the shares is done as guest, and thus you must
+have a valid guest account.
+
+Also, a lot of people are getting bitten by the problem of too many
+parameters on the command line of nmbd in inetd.conf. This trick is to
+not use spaces between the option and the parameter (eg: -d2 instead
+of -d 2), and to not use the -B and -N options. New versions of nmbd
+are now far more likely to correctly find your broadcast and network
+addess, so in most cases these aren't needed.
+
+The other big problem people have is that their broadcast address,
+netmask or IP address is wrong (specified with the -B, -N and -I
+options to nmbd). 
+
+FORCING SAMBA TO BE THE MASTER
+==============================
+
+Who becomes the "master browser" is determined by an election process
+using broadcasts. Each election packet contains a number of parameters
+which determine what precedence (bias) a host should have in the
+election. By default Samba uses a very low precedence and thus loses
+elections to just about anyone else.
+
+If you want Samba to win elections then just set the "os level" global
+option in smb.conf to a higher number. It defaults to 0. Using 33
+would make it win all elections over every other system (except other
+samba systems!)
+
+A "os level" of 2 would make it beat WfWg and Win95, but not NTAS. A
+NTAS domain controller uses level 32.
+
+The maximum os level is 255
+
+MAKING SAMBA THE DOMAIN MASTER
+==============================
+
+The domain master is responsible for collating the browse lists of
+multiple subnets so that browsing can occur between subnets. You can
+make samba act as the domain master by setting "domain master = yes"
+in smb.conf. By default it will not be a domain master.
+
+When samba is the domain master and the master browser it will listen
+for master announcements from other subnets and then contact them to
+synchronise browse lists.
+
+If you want samba to be the domain master then I suggest you also set
+the "os level" high enough to make sure it wins elections.
+
+NOTIFYING THE DOMAIN CONTROLLER
+===============================
+
+If you have a domain controller for the domain which Samba is a part
+of then you should add the line "domain controller = address" to
+smb.conf. "address" can either be a name available via DNS or a IP
+address or a broadcast address. If it is a broadcast address then
+Samba will look for a domain controller on that network.
+
+When Samba is the master browser it will regularly contact the domain
+controller to synchronise browse lists.
+
+
+NOTE ABOUT BROADCAST ADDRESSES
+==============================
+
+If your network uses a "0" based broadcast address (for example if it
+ends in a 0) then you will strike problems. Windows for Workgroups
+does not seem to support a 0's broadcast and you will probably find
+that browsing and name lookups won't work.
+
+You have a few options:
+
+1) change to a 1's broadcast on your unix server. These often end in
+.255 (check with your local network guru for details)
+
+2) set the nmbd broadcast to a 1's based address on the command line using
+the -B option. This only works if your network setup listens on both
+0s and 1s based broadcasts. The -B option can only control what
+address it sends to, not what it listens on.
+
+
diff --git a/docs/textdocs/BUGS.txt b/docs/textdocs/BUGS.txt
new file mode 100644 (file)
index 0000000..e0fd695
--- /dev/null
@@ -0,0 +1,123 @@
+This file describes how to report Samba bugs. 
+
+>> The email address for bug reports is samba-bugs@anu.edu.au <<
+
+(NOTE: This mail may not be in place yet. If you have troubles with it
+then use samba-bugs@arvidsjaur.anu.edu.au)
+
+
+Please take the time to read this file before you submit a bug
+report. Also, please see if it has changed between releases, as I
+may be changing the bug reporting mechanism sometime soon.
+
+Please also do as much as you can yourself to help track down the
+bug. I only develop Samba in my spare time and I receive far more mail
+about it than I can possibly answer, so you have a much higher chance
+of an answer and a fix if you send me a "developer friendly" bug
+report that lets me fix it fast. 
+
+Do not assume that if you post the bug to the comp.protocols.smb
+newsgroup that I will read it. I do read all postings to the samba
+mailing list (see the README). If you suspect that your problem is not
+a bug but a configuration problem then it is better to send it to the
+Samba mailing list, as there are (at last count) 1900 other users on
+that list that may be able to help you.
+
+You may also like to look though the recent mailing list archives,
+which are conveniently accessible on the Samba web pages
+at http://lake.canberra.edu.au/pub/samba/ 
+
+
+GENERAL INFO
+------------
+
+Before submitting a bug report check your config for silly
+errors. Look in your log files for obvious messages that tell you that
+you've misconfigured something and run testparm to test your config
+file for correct syntax.
+
+If you include part of a log file with your bug report then be sure to
+annotate it with exactly what you were doing on the client at the
+time, and exactly what the results were.
+
+
+DEBUG LEVELS
+------------
+
+If the bug has anything to do with Samba behaving incorrectly as a
+server (like refusing to open a file) then the log files will probably
+be very useful. Depending on the problem a log level of between 3 and
+10 showing the problem may be appropriate. A higher level givesmore
+detail, but may use too much disk space.
+
+To set the debug level use "log level =" in your smb.conf. You may
+also find it useful to set the log level higher for just one machine
+and keep separate logs for each machine. To do this use:
+
+log file = /usr/local/samba/lib/log.%m
+include = /usr/local/samba/lib/smb.conf.%m
+
+then create a file "/usr/local/samba/lib/smb.conf.machine" where
+"machine" is the name of the client you wish to debug. In that file
+put any smb.conf commands you want, for example "log level=" may be
+useful. This also allows you to experiment with different security
+systems, protocol levels etc on just one machine.
+
+
+INTERNAL ERRORs
+---------------
+
+If you get a "INTERNAL ERROR" message in your log files it means that
+Samba got an unexpected signal while running. It is probably a
+segmentation fault and almost certainly means a bug in Samba (unless
+you have faulty hardware or system software)
+
+If the message came from smbd then it will probably be accompanied by
+a message which details the last SMB message received by smbd. This
+info is often very useful in tracking down the problem so please
+include it in your bug report.
+
+You should also detail how to reproduce the problem, if
+possible. Please make this reasonably detailed.
+
+You may also find that a core file appeared in a "corefiles"
+subdirectory of the directory where you keep your samba log
+files. This file is the most useful tool for tracking down the bug. To
+use it you do this:
+
+gdb smbd core
+
+adding appropriate paths to smbd and core so gdb can find them. If you
+don't have gdb then try "dbx". Then within the debugger use the
+command "where" to give a stack trace of where the problem
+occurred. Include this in your mail.
+
+If you known any assembly language then do a "disass" of the routine
+where the problem occurred (if its in a library routine then
+disassemble the routine that called it) and try to work out exactly
+where the problem is by looking at the surrounding code. Even if you
+don't know assembly then incuding this info in the bug report can be
+useful. 
+
+
+ATTACHING TO A RUNNING PROCESS
+------------------------------
+
+Unfortunately some unixes (in particular some recent linux kernels)
+refuse to dump a core file if the task has changed uid (which smbd
+does often). To debug with this sort of system you could try to attach
+to the running process using "gdb smbd PID" where you get PID from
+smbstatus. Then use "c" to continue and try to cause the core dump
+using the client. The debugger should catch the fault and tell you
+where it occurred.
+
+
+PATCHES
+-------
+
+The best sort of bug report is one that includes a fix! If you send me
+patches please use "diff -u" format if your version of diff supports
+it, otherwise use "diff -c4". Make sure your do the diff against a
+clean version of the source and let me know exactly what version you
+used. 
+
diff --git a/docs/textdocs/DIAGNOSIS.txt b/docs/textdocs/DIAGNOSIS.txt
new file mode 100644 (file)
index 0000000..6681bdc
--- /dev/null
@@ -0,0 +1,237 @@
+DIAGNOSING YOUR SAMBA SERVER
+============================
+
+This file contains a list of tests you can perform to validate your
+Samba server. It also tells you what the likely cause of the problem
+is if it fails any one of these steps. If it passes all these tests
+then it is probably working fine.
+
+You should do ALL the tests, in the order shown. I have tried to
+carefully choose them so later tests only use capabilities verified in
+the earlier tests.
+
+I would welcome additions to this set of tests. Please mail them to
+samba-bugs@anu.edu.au
+
+If you send me an email saying "it doesn't work" and you have not
+followed this test procedure then you should not be surprised if I
+ignore your email.
+
+
+ASSUMPTIONS
+-----------
+
+In all of the tests I assume you have a Samba server called BIGSERVER
+and a PC called ACLIENT. I also assume the PC is running windows for
+workgroups with a recent copy of the microsoft tcp/ip stack. The
+procedure is similar for other types of clients.
+
+I also assume you know the name of a available share in your
+smb.conf. I will assume this share is called "tmp". You can add a
+"tmp" share like by adding the following to smb.conf:
+
+[tmp]
+ comment = temporary files 
+ path = /tmp
+ read only = yes
+
+
+THESE TESTS ASSUME VERSION 1.9.15 OR LATER OF THE SAMBA SUITE. SOME
+COMMANDS SHOWN DID NOT EXIST IN EARLIER VERSIONS
+
+
+TEST 1:
+-------
+
+run the command "testparm". If it reports any errors then your
+smb.conf configuration file is faulty.
+
+
+TEST 2:
+-------
+
+run the command "ping BIGSERVER" from the PC and "ping ACLIENT" from
+the unix box. If you don't get a valid response then your TCP/IP
+software is not correctly installed. 
+
+Note that you will need to start a "dos prompt" window on the PC to
+run ping.
+
+If you get a message saying "host not found" or similar then your DNS
+software or /etc/hosts file is not correctly setup. It is possible to
+run samba without DNS entries for the server and client, but I assume
+you do have correct entries for the remainder of these tests.
+
+
+TEST 3:
+-------
+
+run the command "smbclient -L BIGSERVER -U%" on the unix box. You
+should get a list of available shares back. 
+
+If you get a error message containing the string "Bad password" then
+you probably have either an incorrect "hosts allow", "hosts deny" or
+"valid users" line in your smb.conf, or your guest account is not
+valid. Check what your guest account is using "testparm" and
+temporarily remove any "hosts allow", "hosts deny", "valid users" or
+"invalid users" lines.
+
+If you get a "connection refused" response then the smbd server could
+not be run. If you installed it in inetd.conf then you probably edited
+that file incorrectly. If you installed it as a daemon then check that
+it is running, and check that the netbios-ssn port is in a LISTEN
+state using "netstat -a".
+
+If you get a "session request failed" then the server refused the
+connection. If it says "your server software is being unfriendly" then
+its probably because you have invalid command line parameters to smbd,
+or a similar fatal problem with the initial startup of smbd. Also
+check your config file for syntax errors with "testparm".
+
+TEST 4:
+-------
+
+run the command "nmblookup -B BIGSERVER __SAMBA__". You should get the
+IP address of your Samba server back.
+
+If you don't then nmbd is incorrectly installed. Check your inetd.conf
+if yu run it from there, or that the daemon is running and listening
+to udp port 137.
+
+One common problem is that many inetd implementations can't take many
+parameters on the command line. If this is the case then create a
+one-line script that contains the right parameters and run that from
+inetd.
+
+TEST 5:
+-------
+
+run the command "nmblookup -B ACLIENT '*'"
+
+You should get the PCs IP address back. If you don't then the client
+software on the PC isn't installed correctly, or isn't started, or you
+got the name of the PC wrong. Note that you probably won't get a "node
+status response" from the PC due to a bug in the microsoft netbios
+nameserver implementation (it responds to the wrong port number).
+
+TEST 6:
+-------
+
+run the command "nmblookup -d 2 '*'"
+
+This time we are trying the same as the previous test but are trying
+it via a broadcast to the default broadcast address. A number of
+Netbios/TCPIP hosts on the network should respond, although Samba may
+not catch all of the responses in the short time it listens. You
+should see "got a positive name query response" messages from several
+hosts. 
+
+If this doesn't give a similar result to the previous test then
+nmblookup isn't correctly getting your broadcast address through its
+automatic mechanism. In this case you should experiment with the -B
+option which allows you to manually specify the broadcast address,
+overriding the automatic detection. You should try different broadcast
+addresses until your find the one that works. It will most likely be
+something like a.b.c.255 as microsoft tcpip stacks only listen on 1's
+based broadcast addresses. If you get stuck then ask your local
+networking guru for help (and show them this paragraph).
+
+If you find you do need the -B option (ie. the automatic detection
+doesn't work) then you should add the -B option with the right
+broadcast address for your network to the command line of nmbd in
+inetd.conf or in the script you use to start nmbd as a daemon. Once
+you do this go back to the "nmblookup __SAMBA__ -B BIGSERVER" test to
+make sure you have it running properly.
+
+If your PC and server aren't on the same subnet then you will need to
+use the -B option to set the broadcast address to the that of the PCs
+subnet.
+
+TEST 7:
+-------
+
+run the command "smbclient '\\BIGSERVER\TMP'". You should then be
+prompted for a password. You should use the password of the account
+you are logged into the unix box with. If you want to test with
+another account then add the -U <accountname> option to the command
+line. 
+
+Once you enter the password you should get the "smb>" prompt. If you
+don't then look at the error message. If it says "invalid network
+name" then the service "tmp" is not correctly setup in your smb.conf.
+
+If it says "bad password" then the likely causes are:
+
+- you have shadow passords (or some other password system) but didn't
+compile in support for them in smbd
+- your "valid users" configuration is incorrect
+- you have a mixed case password and you haven't enabled the "password
+level" option at a high enough level
+- the "path =" line in smb.conf is incorrect. Check it with testparm
+
+Once connected you should be able to use the commands "dir" "get"
+"put" etc. Type "help <command>" for instructions. You should
+especially check that the amount of free disk space shown is correct
+when you type "dir".
+
+
+TEST 8:
+-------
+
+On the PC type the command "net view \\BIGSERVER". You will need to do
+this from within a "dos prompt" window. You should get back a list of
+available shares on the server.
+
+If you get a "network name not found" or similar error then netbios
+name resolution is not working. This is usually caused by a problem in
+nmbd. To overcome it you could do one of the following (you only need
+to choose one of them):
+
+- fixup the nmbd installation
+- add the IP address of BIGSERVER to the "wins server" box in the
+advanced tcp/ip setup on the PC.
+- enable windows name resolution via DNS in the advanced section of
+the tcp/ip setup
+- add BIGSERVER to your lmhosts file on the PC.
+
+If you get a "invalid network name" or "bad password error" then the
+same fixes apply as they did for the "smbclient -L" test above. In
+particular, make sure your "hosts allow" line is correct (see the man
+pages)
+
+
+TEST 9:
+--------
+
+run the command "net use x: \\BIGSERVER\TMP". You should be prompted
+for a password then you should get a "command completed successfully"
+message. If not then your PC software is incorrectly installed or your
+smb.conf is incorrect. make sure your "hosts allow" and other config
+lines in smb.conf are correct.
+
+It's also possible that the server can't work out what user name to
+connect you as. To see if this is the problem add the line "user =
+USERNAME" to the [tmp] section of smb.conf where "USERNAME" is the
+username corresponding to the password you typed. If you find this
+fixes things you may need the username mapping option.
+
+
+TEST 10:
+--------
+
+From file manager try to browse the server. Your samba server should
+appear in the browse list of your local workgroup (or the one you
+specified in the Makefile). You should be able to double click on the
+name of the server and get a list of shares. If you get a "invalid
+password" error when you do then you are probably running WinNT and it
+is refusing to browse a server that has no encrypted password
+capability and is in user level security mode.
+
+
+Still having troubles?
+----------------------
+
+Try the mailing list or newsgroup, or use the tcpdump-smb utility to
+sniff the problem.
+
+
diff --git a/docs/textdocs/DNIX.txt b/docs/textdocs/DNIX.txt
new file mode 100644 (file)
index 0000000..51005e6
--- /dev/null
@@ -0,0 +1,69 @@
+DNIX has a problem with seteuid() and setegid(). These routines are
+needed for Samba to work correctly, but they were left out of the DNIX
+C library for some reason.
+
+For this reason Samba by default defines the macro NO_EID in the DNIX
+section of includes.h. This works around the problem in a limited way,
+but it is far from ideal, some things still won't work right.
+
+To fix the problem properly you need to assemble the following two
+functions and then either add them to your C library or link them into
+Samba.
+
+put this in the file setegid.s:
+
+        .globl  _setegid
+_setegid:
+        moveq   #47,d0
+        movl    #100,a0
+        moveq   #1,d1
+        movl    4(sp),a1
+        trap    #9
+        bccs    1$
+        jmp     cerror
+1$:
+        clrl    d0
+        rts
+
+
+put this in the file seteuid.s:
+
+        .globl  _seteuid
+_seteuid:
+        moveq   #47,d0
+        movl    #100,a0
+        moveq   #0,d1
+        movl    4(sp),a1
+        trap    #9
+        bccs    1$
+        jmp     cerror
+1$:
+        clrl    d0
+        rts
+
+after creating the above files you then assemble them using
+
+as seteuid.s
+as setegid.s
+
+that should produce the files seteuid.o and setegid.o
+
+then you need to add these to the LIBSM line in the DNIX section of
+the Samba Makefile. Your LIBSM line will then look something like this:
+
+LIBSM = setegid.o seteuid.o -ln
+
+You should then remove the line:
+
+#define NO_EID
+
+from the DNIX section of includes.h
+
+Then recompile and try it out!
+
+Note that this file was derived from an email from Peter Olsson
+<pol@leissner.se>. I don't have DNIX myself, so you're probably better
+off contacting Peter if you have problems.
+
+Andrew
+
diff --git a/docs/textdocs/DOMAIN.txt b/docs/textdocs/DOMAIN.txt
new file mode 100644 (file)
index 0000000..31e1967
--- /dev/null
@@ -0,0 +1,68 @@
+Samba now supports domain logons and network logon scripts. The
+support is still experimental, but it seems to work.
+
+The support is also not complete. Samba does not yet support the
+sharing of the SAM database with other systems yet, or remote
+administration. Support for these kind of things should be added
+sometime in the future.
+
+The domain support only works for WfWg and Win95 clients. Support for
+NT and OS/2 clients is still being worked on.
+
+Using these features you can make your clients verify their logon via
+the Samba server and make clients run a batch file when they logon to
+the network. The latter is particularly useful.
+
+To use domain logons you need to do the following:
+
+1) Setup nmbd and smbd and configure the smb.conf so that Samba is
+acting as the master browser. See INSTALL.txt and BROWSING.txt for
+details. 
+
+2) create a share called [netlogon] in your smb.conf. This share should
+be readable by all users, and probably should not be writeable. This
+share will hold your network logon scripts.
+
+For example I have used:
+
+   [netlogon]
+    path = /data/dos/netlogon
+    writeable = no
+    guest ok = yes
+
+
+3) in the [global] section of smb.conf set the following:
+
+   domain logons = yes
+   logon script = %U.bat
+
+the choice of batch file is, of course, up to you. The above would
+give each user a separate batch file as the %U will be changed to
+their username automatically. The other standard % macros may also be
+used. You can make the btch files come from a subdirectory by using
+soemthing like:
+
+   logon script = scripts\%U.bat
+
+4) create the batch files to be run when the user logs in. If the batch
+file doesn't exist then no batch file will be run. 
+
+In the batch files you need to be careful to use DOS style cr/lf line
+endings. If you don't then DOS may get confused. I suggest you use a
+DOS editor to remotely edit the files if you don't know how to produce
+DOS style files under unix.
+
+5) Use smbclient with the -U option for some users to make sure that
+the \\server\NETLOGON share is available, the batch files are visible
+and they are readable by the users.
+
+6) you will probabaly find that your clients automatically mount the
+\\SERVER\NETLOGON share as drive z: while logging in. You can put some
+useful programs there to execute from the batch files.
+
+
+NOTE: You must be using "security = user" or "security = server" for
+domain logons to work correctly. Share level security won't work
+correctly.
+
+
diff --git a/docs/textdocs/ENCRYPTION.txt b/docs/textdocs/ENCRYPTION.txt
new file mode 100644 (file)
index 0000000..046b473
--- /dev/null
@@ -0,0 +1,333 @@
+               LanManager / Samba Password Encryption.
+               ---------------------------------------
+
+With the development of LanManager compatible password encryption for
+Samba, it is now able to validate user connections in exactly the same
+way as a LanManager or Windows NT server.
+
+This document describes how the SMB password encryption algorithm
+works and what issues there are in choosing whether you want to use
+it. You should read it carefully, especially the part about security
+and the "PROS and CONS" section.
+
+How does it work ?
+------------------
+
+       LanManager encryption is somewhat similar to UNIX password
+encryption. The server uses a file containing a hashed value of a
+users password.  This is created by taking the users paintext
+password, capitalising it, and either truncating to 14 bytes (or
+padding to 14 bytes with null bytes). This 14 byte value is used as
+two 56 bit DES keys to encrypt a 'magic' eight byte value, forming a
+16 byte value which is stored by the server and client. Let this value
+be known as the *hashed password*.
+
+When a client (LanManager, Windows for WorkGroups, Windows 95 or
+Windows NT) wishes to mount a Samba drive (or use a Samba resource) it
+first requests a connection and negotiates the protocol that the client
+and server will use. In the reply to this request the Samba server
+generates and appends an 8 byte, random value - this is stored in the
+Samba server after the reply is sent and is known as the *challenge*.
+
+The challenge is different for every client connection.
+
+The client then uses the hashed password (16 byte value described
+above), appended with 5 null bytes, as three 56 bit DES keys, each of
+which is used to encrypt the challenge 8 byte value, forming a 24 byte
+value known as the *response*.
+
+In the SMB call SMBsessionsetupX (when user level security is
+selected) or the call SMBtconX (when share level security is selected)
+the 24 byte response is returned by the client to the Samba server.
+
+The Samba server then reproduces the above calculation, using it's own
+stored value of the 16 byte hashed password (read from the smbpasswd
+file - described later) and the challenge value that it kept from the
+negotiate protocol reply. It then checks to see if the 24 byte value it
+calculates matches the 24 byte value returned to it from the client.
+
+If these values match exactly, then the client knew the correct
+password (or the 16 byte hashed value - see security note below) and
+is this allowed access. If not then the client did not know the
+correct password and is denied access.
+
+Note that the Samba server never knows or stores the cleartext of the
+users password - just the 16 byte hashed function derived from it. Also
+note that the cleartext password or 16 byte hashed value are never
+transmitted over the network - thus increasing security.
+
+IMPORTANT NOTE ABOUT SECURITY
+-----------------------------
+
+The unix and SMB password encryption techniques seem similar on the
+surface. This similarity is, however, only skin deep. The unix scheme
+typically sends clear text passwords over the nextwork when logging
+in. This is bad. The SMB encryption scheme never sends the cleartext
+password over the network but it does store the 16 byte hashed value
+on disk. This is also bad. Why? Because the 16 byte hashed value is a
+"password equivalent". You cannot derive the users password from it,
+but it could potentially be used in a modified client to gain access
+to a server. This would require considerable technical knowledge on
+behalf of the attacker but is perfectly possible. You should thus
+treat the smbpasswd file as though it contained the cleartext
+passwords of all your users. Its contents must be kept secret, and the
+file should be protected accordingly.
+
+Ideally we would like a password scheme which neither requires plain
+text passwords on the net or on disk. Unfortunately this is not
+available as Samba is stuck with being compatible with other SMB
+systems (WinNT, WfWg, Win95 etc). 
+
+
+PROS AND CONS
+-------------
+
+There are advantages and disadvantages to both schemes. 
+
+Advantages of SMB Encryption:
+-----------------------------
+
+- plain text passwords are not passed across the network. Someone using
+a network sniffer cannot just record passwords going to the SMB server.
+
+- WinNT doesn't like talking to a server that isn't using SMB
+encrypted passwords. It will refuse to browse the server if the server
+is also in user level security mode. It will insist on promting the
+user for the password on each connection, which is very annoying. The
+only things you can do to stop this is to use SMB encryption.
+
+Advantages of non-encrypted passwords:
+--------------------------------------
+
+- plain text passwords are not kept on disk. 
+
+- uses same password file as other unix services such as login and
+ftp
+
+- you are probably already using other services (such as telnet and
+ftp) which send plain text passwords over the net, so not sending them
+for SMB isn't such a big deal.
+
+- the SMB encryption code in Samba is new and has only had limited
+testing. We have tried hard to make it secure but in any new
+implementation of a password scheme there is the possability of an
+error. 
+
+
+The smbpasswd file.
+-------------------
+
+       In order for Samba to participate in the above protocol it must
+be able to look up the 16 byte hashed value given a user name.
+Unfortunately, as the UNIX password value is also a one way hash
+function (ie. it is impossible to retrieve the cleartext of the users
+password given the UNIX hash of it) then a separate password file
+containing this 16 byte value must be kept. To minimise problems with
+these two password files, getting out of sync, the UNIX /etc/passwd and
+the smbpasswd file, a utility, mksmbpasswd.sh, is provided to generate
+a smbpasswd file from a UNIX /etc/passwd file.
+
+To generate the smbpasswd file from your /etc/passwd file use the
+following command :-
+
+cat /etc/passwd | mksmbpasswd.sh >/usr/local/samba/private/smbpasswd
+
+If you are running on a system that uses NIS, use
+
+ypcat passwd | mksmbpasswd.sh >/usr/local/samba/private/smbpasswd
+
+The mksmbpasswd.sh program is found in the Samba source directory. By
+default, the smbpasswd file is stored in :-
+
+/usr/local/samba/private/smbpasswd
+
+The owner of the /usr/local/samba/private directory should be set to
+root, and the permissions on it should be set to :-
+
+r-x------
+
+The command 
+
+chmod 500 /usr/local/samba/private
+
+will do the trick. Likewise, the smbpasswd file inside the private
+directory should be owned by root and the permissions on is should be
+set to
+
+rw-------
+
+by the command :-
+
+chmod 600 smbpasswd.
+
+The format of the smbpasswd file is
+
+username:uid:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:Long name:user home dir:user shell
+
+Although only the username, uid, and XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+sections are significant and are looked at in the Samba code.
+
+It is *VITALLY* important that there by 32 'X' characters between the
+two ':' characters - the smbpasswd and Samba code will fail to validate
+any entries that do not have 32 characters between ':' characters.
+
+When the password file is created all users have password entries
+consisting of 32 'X' characters. By default this disallows any access
+as this user. When a user has a password set, the 'X' characters change
+to 32 ascii hexadecimal digits (0-9, A-F). These are an ascii
+representation of the 16 byte hashed value of a users password.
+
+To set a user to have no password (not recommended), edit the file
+using vi, and replace the first 11 characters with the asci text
+
+NO PASSWORD
+
+Eg. To clear the password for user bob, his smbpasswd file entry would
+look like :
+
+bob:100:NO PASSWORDXXXXXXXXXXXXXXXXXXXXX:Bob's full name:/bobhome:/bobshell
+
+If you are allowing users to use the smbpasswd command to set their own
+passwords, you may want to give users NO PASSWORD initially so they do
+not have to enter a previous password when changing to their new
+password (not recommended).
+
+Note : This file should be protected very carefully. Anyone with
+access to this file can (with enough knowledge of the protocols) gain
+access to your SMB server. The file is thus more sensitive than a
+normal unix /etc/passwd file.
+
+The smbpasswd Command.
+----------------------
+
+       The smbpasswd command maintains the 32 byte password field in
+the smbpasswd file. If you wish to make it similar to the unix passwd
+or yppasswd programs, install it in /usr/local/samba/bin (or your main
+Samba binary directory) and make it setuid root.
+
+Note that if you do not do this then the root user will have to set all
+users passwords.
+
+To set up smbpasswd as setuid root, change to the Samba binary install
+directory and then type (as root) :
+
+chown root smbpasswd
+chmod 4555 smbpasswd
+
+If smbpasswd is installed as setuid root then you would use it as
+follows.
+
+smbpasswd
+Old SMB password: <type old alue here - just hit return if there is NO PASSWORD>
+New SMB Password: < type new value >
+Repeat New SMB Password: < re-type new value >
+
+If the old value does not match the current value stored for that user,
+or the two new values do not match each other, then the password will
+not be changed.
+
+If invoked by an ordinary user it will only allow the user to change
+his or her own Samba password.
+
+If run by the root user smbpasswd may take an optional argument,
+specifying the user name whose SMB password you wish to change.  Note
+that when run as root smbpasswd does not prompt for or check the old
+password value, thus allowing root to set passwords for users who have
+forgotten their passwords.
+
+smbpasswd is designed to work in the same way and be familiar to UNIX
+users who use the passwd or yppasswd commands.
+
+NOTE. As smbpasswd is designed to be installed as setuid root I would
+appreciate it if everyone examined the source code to look for
+potential security flaws. A setuid program, if not written properly can
+be an open door to a system cracker. Please help make this program
+secure by reporting all problems to me (the author, Jeremy Allison).
+
+My email address is :-
+
+jra@vantive.com
+
+Setting up Samba to support LanManager Encryption.
+--------------------------------------------------
+
+This is a very brief description on how to setup samba to support
+password encryption. More complete instructions will probably be added
+later.
+
+1) get and compile the libdes libraries. the source is available from
+nimbus.anu.edu.au in pub/tridge/libdes/libdes.tar.92-10-13.gz
+
+2) enable the encryption stuff in the Samba makefile, making sure you
+point it to the libdes library and include file (it needs des.h)
+The entries you need to uncomment are the four lines after the comment :-
+
+# This is for SMB encrypted (lanman) passwords.
+
+Note that you may have to change the variable DES_BASE to
+point at the place where you installed the DES library.
+
+3) compile and install samba as usual
+
+4) f your system can't compile the module getsmbpass.c then remove the
+-DSMBGETPASS define from the Makefile.
+
+5) enable encrypted passwords in smb.conf by adding the line 
+"encrypt passwords = yes" in the [global] section
+
+6) create the initial smbpasswd password file in the place you
+specified in the Makefile. A simple way to do this based on your
+existing Makefile (assuming it is in a reasonably standard format) is
+like this:
+
+cat /etc/passwd | mksmbpasswd.sh > /usr/local/samba/private/smbpasswd
+
+Change ownership of private and smbpasswd to root.
+
+chown -R root /usr/local/samba/private
+
+Set the correct permissions on /usr/local/samba/private
+
+chmod 500 /usr/local/samba/private
+
+Set the correct permissions on /usr/local/samba/private/smbpasswd
+
+chmod 600 /usr/local/samba/private/smbpasswd
+
+note that the mksmbpasswd.sh script is in the samba source directory.
+
+If this fails then you will find that you will need entries that look
+like this:
+
+# SMB password file.
+tridge:148:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:Andrew Tridgell:/home/tridge:/bin/tcsh
+
+note that the uid and username fields must be right. Also, you must get
+the number of X's right (there should be 32).
+
+If you wish, install the smbpasswd program as suid root.
+
+chown root /usr/local/samba/bin/smbpasswd
+chmod 4555 /usr/local/samba/bin/smbpasswd
+
+7) set the passwords for users using the smbpasswd command. For
+example, as root you could do "smbpasswd tridge"
+
+8) try it out!
+
+Note that you can test things using smbclient, as it also now supports
+encryption.
+
+NOTE TO USA Sites that Mirror Samba
+-----------------------------------
+
+The DES library is considered a munition in the USA. Under US Law it is
+illegal to export this software, or to put it in a freely available ftp
+site.
+
+Please do not mirror the DES directory from the site on nimbus.anu.edu.au
+
+Thank you,
+
+Jeremy Allison.
+
diff --git a/docs/textdocs/HINTS.txt b/docs/textdocs/HINTS.txt
new file mode 100644 (file)
index 0000000..953650b
--- /dev/null
@@ -0,0 +1,202 @@
+Here are some random hints that you may find useful. These really
+should be incorporated in the main docs someday.
+
+
+----------------------
+HINT: Always test your smb.conf with testparm before using it
+
+If your smb.conf file is invalid then samba will fail to load. Run
+testparm over it before you install it just to make sure there aren't
+any basic syntax or logical errors.
+
+
+----------------------
+HINT: Try printing with smbclient first
+
+If you have problems printing, test with smbclient first. Just connect using 
+"smbclient '\\server\printer' -P" and use the "print" command.
+
+Once this works, you know that Samba is setup correctly for printing,
+and you should be able to get it to work from your PCs.
+
+This particularly helps in getting the "print command" right.
+
+
+----------------------
+HINT: Mount cdroms with conv=binary
+
+Some OSes (notably Linux) default to auto detection of file type on
+cdroms and do cr/lf translation. This is a very bad idea when use with
+Samba. It causes all sorts of stuff ups.
+
+To overcome this problem use conv=binary when mounting the cdrom
+before exporting it with Samba.
+
+
+----------------------
+HINT: Convert between unix and dos text formats
+
+Jim barry has written an excellent drag-and-drop cr/lf converter for
+windows. Just drag your file onto the icon and it converts the file.
+
+Get it from
+ftp://nimbus.anu.edu.au/pub/tridge/samba/contributed/fixcrlf.zip
+
+---------------------- 
+HINT: Use the "username map" option
+
+If the usernames used on your PCs don't match those used on the unix
+server then you will find the "username map" option useful.
+
+-----------------------
+HINT: Use "security = user" in [global]
+
+If you have the same usernames on the unix box and the PCs or have
+mapped them with the "username map" option then choose "security =
+user" in the [global] section of smb.conf.
+
+This will mean your password is checked only when you first connect,
+and subsequent connections to printers, disks etc will go more
+smoothly and much faster.
+
+The main problem with "security = user" if you use WfWg is that you
+will ONLY be able to connect as the username that you log into WfWg
+with. This is because WfWg silently ignores the password field in the
+connect drive dialog box if the server is in user security mode.
+
+------------------------
+HINT: Make your printers not "guest ok"
+
+If your printers are not "guest ok" and you are using "security =
+user" and have matching unix and PC usernames then you will attach to
+the printer without trouble as your own username. This will mean you
+will be able to delete print jobs (in 1.8.06 and above) and printer
+accounting will be possible.
+
+
+-----------------------
+HINT: Use a sensible "guest" account
+
+Even if all your services are not available to "guest" you will need a
+guest account. This is because the browsing is done as guest. In many
+cases setting "guest account = ftp" will do the trick. Using the
+default guest account or "guest account = nobody" will give problems on
+many unixes. If in doubt create another account with minimal
+privilages and use it instead. Your users don't need to know the
+password of the guest account.
+
+
+-----------------------
+HINT: Use the latest TCP/IP stack from microsoft if you use Windows
+for workgroups.
+
+The early TCP/IP stacks had lots of bugs.
+
+Microsoft has released an incremental upgrade to their TCP/IP 32-Bit
+VxD drivers.  The latest release can be found on their ftp site at
+ftp.microsoft.com, located in /peropsys/windows/public/tcpip/wfwt32.exe.
+There is an update.txt file there that describes the problems that were
+fixed.  New files include WINSOCK.DLL, TELNET.EXE, WSOCK.386, VNBT.386,
+WSTCP.386, TRACERT.EXE, NETSTAT.EXE, and NBTSTAT.EXE.
+
+
+-----------------------
+HINT: nmbd can act as a "WINS" server
+
+By default SMB clients use broadcasts to find shares. Recent clients
+(such as WfWg) can use a "wins" server instead, whcih reduces your
+broadcast traffic and allows you to find names across routers.
+
+Just point your WfWg, Win95 and NT clients at the Samba box in the WINS option.
+
+Note: nmbd does not support all WINS operations. Anyone out there have
+a spec they could send me?
+
+-----------------------
+HINT: you may need to delete your .pwl files when you change password.
+
+WfWg does a lousy job with passwords. I find that if I change my
+password on either the unix box or the PC the safest thing to do is to
+delete the .pwl files in the windows directory. The PC will complain about not finding the files, but will soon get over it, allowing you to enter the new password.
+
+If you don't do this you may find that WfWg remembers and uses the old
+password, even if you told it a new one.
+
+Often WfWg will totally ignore a password you give it in a dialog box.
+
+----------------------
+HINT: Using MS Access
+
+Here are some notes on running MS-Access on a Samba drive from Stefan 
+Kjellberg <stefank@esi.com.au>
+
+1. Opening a database in 'exclusive' mode does NOT work. Samba ignores
+   r/w/share modes on file open.
+
+2. Make sure that you open the database as 'shared' and to 'lock modified
+   records'
+
+3. Of course locking must be enabled for the particular share (smb.conf)
+
+
+---------------------
+HINT: password cacheing in WfWg
+
+Here is a hint from michael@ecel.uwa.edu.au (Michael Simmons):
+
+In case people where not aware. There is a program call admincfg.exe
+on the last disk (disk 8) of the WFW 3.11 disk set.  To install it
+type EXPAND A:\ADMINCFG.EX_ C:\WINDOWS\ADMINCFG.EXE Then add an icon
+for it via the "Progam Manager" "New" Menu.  This program allows you
+to control how WFW handles passwords.  ie disable Password Caching etc
+for use with "security = user"
+
+
+--------------------
+HINT: file descriptor limits
+
+If you have problems with the limits on the number of open files you
+can edit local.h to fix it.
+
+--------------------
+HINT: HPUX initgroups() problem
+
+here is a hint from Frank Wales [frank@arcglade.demon.co.uk]:
+
+HP's implementation of supplementary groups is, er, non-standard (for
+hysterical reasons).  There are two group files, /etc/group and
+/etc/logingroup; the system maps UIDs to numbers using the former, but
+initgroups() reads the latter.  Most system admins who know the ropes
+symlink /etc/group to /etc/logingroup (hard link doesn't work for reasons
+too stupid to go into here).  initgroups() will complain if one of the
+groups you're in in /etc/logingroup has what it considers to be an invalid
+ID, which means outside the range [0..UID_MAX], where UID_MAX is (I think)
+60000 currently on HP-UX.  This precludes -2 and 65534, the usual 'nobody'
+GIDs.
+
+Perhaps you could suggest to users that, if they encounter this problem,
+they make sure that the programs that are failing to initgroups() be
+run as users not in any groups with GIDs outside the allowed range.
+
+This is documented in the HP manual pages under setgroups(2) and passwd(4).
+
+
+---------------------
+HINT: Patch your SCO system
+
+If you run SCO Unix then you may need to get important TCP/IP patches
+for Samba to work correctly. Try 
+
+Paul_Davis@mindlink.bc.ca writes:
+
+  I was having problems with Accpac using 1.9.02 on SCO Unix.  One
+  posting function reported corrupted data.  After installing uod385a,
+  the problem went away (a restore from backup and then another
+  run-thru).
+
+  It appears that the uod385a update for SCO may be fairly important for
+  a lot of different DOS and Windows software under Samba.
+
+  uod385a can be found at ftp.sco.com /SLS/uod385a.Z and uod385a.ltr.Z.
+
+
diff --git a/docs/textdocs/INSTALL.sambatar b/docs/textdocs/INSTALL.sambatar
new file mode 100644 (file)
index 0000000..388e2a3
--- /dev/null
@@ -0,0 +1,27 @@
+
+Please see the readme and the man page for general info.
+
+1) Follow the samba installation instructions.
+
+2) If all goes well, test it out by creating a share on your PC (called
+backup for example) then doing something like,
+
+  ./smbtar -s mypc -t /dev/rmt/0ubn -x backup
+
+substituting whatever your tape drive is for the -t option, or set your
+tape environmental variable.
+
+If all does not go well, feel free to mail the author (poultenr@logica.co.uk)
+about bug reports / help / money / pizza / etc.
+
+3) Read the man page and the NOTES file for more information
+
+4) Work smbtar into your usual nightly backup scheme (presuming you
+have one :-}).
+
+
+NOTE:
+
+If you have problems with smbtar then it's probably best to contact the
+author Ricky Poulten (poultenr@logica.co.uk).
+
diff --git a/docs/textdocs/PROJECTS b/docs/textdocs/PROJECTS
new file mode 100644 (file)
index 0000000..cf903f2
--- /dev/null
@@ -0,0 +1,96 @@
+                 Samba Projects Directory
+                 ========================
+
+
+>>>>> NOTE: THIS FILE IS NOW VERY OUT OF DATE <<<<<
+
+
+This is a list of who's working on what in Samba. It's not guaranteed
+to be uptodate or accurate but I hope it will help us getting
+coordinated.
+
+If you are working on something to do with Samba and you aren't here
+then please let me know! Also, if you are listed below and you have
+any corrections or updates then please let me know.
+
+Email contact:
+samba-bugs@anu.edu.au
+
+========================================================================
+Documentation and FAQ
+
+Docs and FAQ files for the Samba suite of software.
+
+Contact Karl.Auer@anu.edu.au
+
+Mark Preston is now working on a set of formatted docs for Samba. 
+Contact mpreston@sghms.ac.uk
+
+Docs are currently up to date with version, 1.7.07. FAQ being added to
+as questions arise.
+
+Status last updated 27th September 1994
+========================================================================
+
+========================================================================
+Netbeui support
+
+This aims to produce patches so that Samba can be used with clients
+that do not have TCP/IP. It will try to remain as portable as possible.
+
+Contact Brian.Onn@Canada.Sun.COM (Brian Onn)
+
+The project is just startup up.
+
+Status last updated 4th October 1994
+========================================================================
+
+========================================================================
+Smbfs
+
+A mountable smb filesystem for Linux using the userfs userspace filesystem
+
+Contact lendecke@namu01.gwdg.de (Volker Lendecke)
+
+Currently this is at version 0.2. It works but is really only for
+people with some knowledge and experience of Linux kernel hacking.
+
+Status last updated 23rd August 1994
+========================================================================
+
+========================================================================
+Nmbd
+
+Aims to produce a complete rfc1001/1002 implementation. The current
+nmbd is a partial implementation.
+
+Contact Fabrice Cetre (cetre@ifhpserv.insa-lyon.fr)
+
+Status last updated 23rd August 1994
+========================================================================
+
+========================================================================
+Admin Tool
+
+Aims to produce a nice smb.conf editor and other useful tools for
+administering a Samba system.
+
+Contact: Steve Brown (steve@unicorn.dungeon.com)
+
+In the design phase.
+
+Status last updated 4th September 1994
+========================================================================
+
+
+========================================================================
+Lanman Client.
+
+Contact: john@amanda.xs4all.nl (John Stewart)
+
+Aims to produce a reliable LANMAN Client implementation for LINUX,
+and possibly other variations of UNIX. Project ably started by
+Tor Lillqvist; tml@hemuli.tte.vtt.fi
+
+Status last updated 17th January 1995
+========================================================================
diff --git a/docs/textdocs/Passwords.txt b/docs/textdocs/Passwords.txt
new file mode 100644 (file)
index 0000000..e06876f
--- /dev/null
@@ -0,0 +1,42 @@
+NOTE ABOUT PASSWORDS
+====================
+
+Unix systems use a wide variety of methods for checking the validity
+of a password. This is primarily controlled with the Makefile defines
+mentioned in the Makefile.
+
+Also note that some clients (notably WfWg) uppercase the password
+before sending it. The server tries the password as it receives it and
+also after lowercasing it.
+
+The Samba server can also be configured to try different
+upper/lowercase combinations. This is controlled by the [global]
+parameter "password level". A level of N means to try all combinations
+up to N uppercase characters in the password. A high value can chew a
+fair bit of CPU time and can lower the security of your system. Do not
+use this options unless you really need it - the time taken for
+password checking can become so high that clients time out. 
+
+If you do use the "password level" option then you might like to use
+-DUFC_CRYPT in your Makefile. On some machine this makes password
+checking _much_ faster. This is also useful if you use the @group
+syntax in the user= option.
+
+If your site uses AFS (the Andrew File System), you can use the AFS section
+in the Makefile.  This will first attempt to authenticate a username and
+password to AFS.  If that succeeds, then the associated AFS rights will be
+granted.  Otherwise, the password checking routine falls back to whatever
+Unix password checking method you are using.  Note that the AFS code is
+only written and tested for AFS 3.3 and later.
+
+
+SECURITY = SERVER
+=================
+
+Samba can use a remote server to do it's username/password
+validation. This allows you to have one central machine (for example a
+NT box) control the passwords for the Unix box.
+
+See the section on "security =" in smb.conf(5) for details.
+
+
diff --git a/docs/textdocs/README.DCEDFS b/docs/textdocs/README.DCEDFS
new file mode 100644 (file)
index 0000000..f84b84b
--- /dev/null
@@ -0,0 +1,79 @@
+=============================================================================
+
+       Basic DCE/DFS Support for SAMBA 1.9.13
+
+       Jim Doyle <doyle@oec.com> 06-02-95
+
+=============================================================================
+
+Functionality:
+--------------
+       
+       Per-instance authentication for DCE/DFS.
+Missing Functionality in this Implementation:
+---------------------------------------------
+
+       * No automatic refresh of credentials
+
+         To do so would not be that hard.. One could simply
+         stash the clear-text key in memory, spawn a key management
+         thread to wake up right before credentials expire and
+         refresh the login context.
+
+       * No UNIX Signals support (SIGCLD, SIGPIPE, SIGHUP, SIGBUS, SIGSEGV)
+
+
+         There is no support for signal processing in Samba daemons
+         that need to authenticate with DCE. The explanation for this
+         is that the smbd is linked against thread-safe libraries in
+         order to be able to use DCE authentication mechanisms. 
+         Because smbd uses signal() and fork(), it represents the
+         worst case scenario for DCE portability. In order
+         to properly support signals in a forked server environment,
+         some rework of smbd is needed in order to properly
+         construct, shutdown and reconstruct asynchronous signal
+         handling threads and synchronous signal traps across the
+         parent and child. I have not had contiguous time to work
+         on it, I expect it to be a weeks worth of work to cleanly
+         integrate thread-safe signal handing into the code and 
+         test it. Until I can get to this task, I will leave it up
+         to someone adventurous enough to engineer it and negotiate
+         with Andrew to integrate the changes into the mainline branch.
+
+         The lack of full signal support means that you cannot
+         rely upon SIGHUP-ing the parent daemon to refresh
+          the configuration data. Likewise, you cannot take advantage
+         of the builtin SIGBUS/SIGSEGV traps to diagnose failures.
+         You will have to halt Samba in order to make changes
+         and then have them take effect.
+
+         The SMBD server as it stands is suitable to use if you
+         already have experience with configuring and running
+         SAMBA.
+
+Tested Platforms:
+-----------------
+
+               HP-UX 9.05 / HP-UX DCE 1.2.1
+               AIX 3.2.5  / AIX DCE/6000 1.3
+               DEC OSF-1 3.0 / DEC DCE 1.3
+
+Building:
+---------
+
+       - Uncomment the the appropriate block in the Makefile
+         for the platform you wish to build on.
+
+       - Samples of Samba server configuration files for our
+         DFS environment are included in samples.dcedfs/
+
+
+
+Bugs, Suggestions, etc..
+--------------------------
+
+       Please post them to the mailing list. 
+       That way I will see them and they will become part of 
+       the archives so others can share the knowledge.
+
diff --git a/docs/textdocs/README.jis b/docs/textdocs/README.jis
new file mode 100644 (file)
index 0000000..2ac6716
--- /dev/null
@@ -0,0 +1,124 @@
+\e$B!|\e(B samba \e$BF|K\8lBP1~$K$D$$$F\e(B
+
+1. \e$BL\E*\e(B
+
+  \e$BF|K\8lBP1~$O!"\e(B
+
+    (1) MS-Windows \e$B>e$G!"4A;z%U%!%$%kL>$r$I$&$7$F$b07$&I,MW$N$"$k%"%W%j%1!<%7%g%s$,$A$c\e(B
+        \e$B$s$HF0:n$9$k!#Nc$($P!"\e(BMS-WORD 5 \e$B$J$I$O!"%$%s%9%H!<%k;~$K4A;z$N%U%!%$%kL>$r>!<j\e(B
+        \e$B$K$D$1$F$7$^$$$^$9!#$3$&$$$C$?>l9g$K$A$c$s$HBP1~$G$-$k$h$&$K$9$k!#\e(B
+
+    (2) UNIX \e$B$O!":G6a$G$O$[$H$s$I$N$b$N$,\e(B 8 bits \e$B$N%U%!%$%kL>$r%5%]!<%H$7$F$$$^$9$,!"\e(B
+        \e$BCf$K$O!"$3$l$r%5%]!<%H$7$F$$$J$$$b$N$b$"$j$^$9!#$3$N$h$&$J>l9g$G$b!"\e(B(1)\e$B$NL\E*\e(B
+        \e$B$,K~B-$G$-$k$h$&$K$9$k!#\e(B
+
+  \e$B$rL\E*$H$7$F$$$^$9!#$=$N$?$a!"F|K\8lBP1~$O!"I,MW:G>.8B$7$+9T$J$C$F$*$j$^$;$s!#\e(B
+
+2. \e$BMxMQJ}K!\e(B
+
+(1) \e$BDI2C$7$?%Q%i%a!<%?\e(B
+
+  smb.conf \e$B%U%!%$%k$N\e(B global \e$B%;%/%7%g%s$K0J2<$N%Q%i%a!<%?$r@_Dj$G$-$k$h$&$K$7$^$7$?!#\e(B
+
+    [global]
+     ....
+     coding system = <\e$B%3!<%I7O\e(B>
+
+  \e$B$3$3$G;XDj$5$l$?%3!<%I7O$,\e(B UNIX \e$B>e$N%U%!%$%k%7%9%F%`$N%U%!%$%kL>$N%3!<%I$K$J$j$^$9!#\e(B
+  \e$B@_Dj$G$-$k$b$N$O!"<!$N$h$&$K$J$C$F$$$^$9!#\e(B
+
+    sjis:  SHIFT JIS (MS \e$B4A;z%3!<%I\e(B) 
+    euc:   EUC \e$B%3!<%I\e(B
+    hex:  7 bits \e$B$N\e(B ASCII \e$B%3!<%I0J30$N%3!<%I$r0J2<$N7A<0$GI=$9J}<0$G$9!#Nc$($P!"\e(B
+         '\e$B%*%U%#%9\e(B' \e$B$H$$$&L>A0$O!"\e(B':83:49:83:74:83:42:83:58' \e$B$N$h$&$K!"\e(B':' \e$B$N8e$K#27e\e(B
+          \e$B$N\e(B16\e$B?J?t$rB3$1$k7A<0$K$J$j$^$9!#\e(B
+         \e$B$3$3$G!"\e(B':' \e$B$rB>$NJ8;z$KJQ99$7$?$$>l9g$O!"\e(Bhex \e$B$N8e$m$K$=$NJ8;z$r;XDj$7$^$9!#\e(B
+          \e$BNc$($P!"\e(B@\e$B$rJQ$o$j$K;H$$$?$$>l9g$O!"\e(B'hex@'\e$B$N$h$&$K;XDj$7$^$9!#\e(B
+    JIS \e$B%3!<%I$K$D$$$F$O!"0J2<$NI=$r;2>H$7$F2<$5$$!#\e(B
+    \e$B(#(!(!(!(((!(!(!(!(((!(!(!(!(((!(!(!(!(((!(!(!(!(((!(!(!(!(((!(!(!(!(!(!(!(!(!($\e(B
+    \e$B(";XDj\e(B  \e$B("4A;z3+;O("4A;z=*N;("%+%J3+;O("%+%J=*N;("1Q?t3+;O("Hw9M\e(B              \e$B("\e(B
+    \e$B('(!(!(!(+(!(!(!(!(+(!(!(!(!(+(!(!(!(!(+(!(!(!(!(+(!(!(!(!(+(!(!(!(!(!(!(!(!(!()\e(B
+    \e$B("\e(Bjis7  \e$B("\e(B\E$B    \e$B("\e(B\E(J    \e$B("\e(B0x0e    \e$B("\e(B0x0f    \e$B("\e(B\E(J    \e$B("\e(Bjis 7\e$BC10LId9f\e(B     \e$B("\e(B
+    \e$B("\e(Bjunet \e$B("\e(B\E$B    \e$B("\e(B\E(J    \e$B("\e(B\E(I    \e$B("\e(B\E(J    \e$B("\e(B\E(J    \e$B("\e(B7bits \e$B%3!<%I\e(B      \e$B("\e(B
+    \e$B("\e(Bjis8  \e$B("\e(B\E$B    \e$B("\e(B\E(J    \e$B("\e(B--      \e$B("\e(B--      \e$B("\e(B\E(J    \e$B("\e(Bjis 8\e$BC10LId9f\e(B     \e$B("\e(B
+    \e$B("\e(Bj7bb  \e$B("\e(B\E$B    \e$B("\e(B\E(B    \e$B("\e(B0x0e    \e$B("\e(B0x0f    \e$B("\e(B\E(B    \e$B("\e(B                  \e$B("\e(B
+    \e$B("\e(Bj7bj  \e$B("\e(B\E$B    \e$B("\e(B\E(J    \e$B("\e(B0x0e    \e$B("\e(B0x0f    \e$B("\e(B\E(J    \e$B("\e(Bjis7\e$B$HF1$8\e(B        \e$B("\e(B
+    \e$B("\e(Bj7bh  \e$B("\e(B\E$B    \e$B("\e(B\E(H    \e$B("\e(B0x0e    \e$B("\e(B0x0f    \e$B("\e(B\E(H    \e$B("\e(B                  \e$B("\e(B
+    \e$B("\e(Bj7@b  \e$B("\e(B\E$@    \e$B("\e(B\E(B    \e$B("\e(B0x0e    \e$B("\e(B0x0f    \e$B("\e(B\E(B    \e$B("\e(B                  \e$B("\e(B
+    \e$B("\e(Bj7@j  \e$B("\e(B\E$@    \e$B("\e(B\E(J    \e$B("\e(B0x0e    \e$B("\e(B0x0f    \e$B("\e(B\E(J    \e$B("\e(B                  \e$B("\e(B
+    \e$B("\e(Bj7@h  \e$B("\e(B\E$@    \e$B("\e(B\E(H    \e$B("\e(B0x0e    \e$B("\e(B0x0f    \e$B("\e(B\E(H    \e$B("\e(B                  \e$B("\e(B
+    \e$B("\e(Bj8bb  \e$B("\e(B\E$B    \e$B("\e(B\E(B    \e$B("\e(B--      \e$B("\e(B--      \e$B("\e(B\E(B    \e$B("\e(B                  \e$B("\e(B
+    \e$B("\e(Bj8bj  \e$B("\e(B\E$B    \e$B("\e(B\E(J    \e$B("\e(B--      \e$B("\e(B--      \e$B("\e(B\E(J    \e$B("\e(Bjis8\e$B$HF1$8\e(B        \e$B("\e(B
+    \e$B("\e(Bj8bh  \e$B("\e(B\E$B    \e$B("\e(B\E(H    \e$B("\e(B--      \e$B("\e(B--      \e$B("\e(B\E(H    \e$B("\e(B                  \e$B("\e(B
+    \e$B("\e(Bj8@b  \e$B("\e(B\E@@    \e$B("\e(B\E(B    \e$B("\e(B--      \e$B("\e(B--      \e$B("\e(B\E(B    \e$B("\e(B                  \e$B("\e(B
+    \e$B("\e(Bj8@j  \e$B("\e(B\E$@    \e$B("\e(B\E(J    \e$B("\e(B--      \e$B("\e(B--      \e$B("\e(B\E(J    \e$B("\e(B                  \e$B("\e(B
+    \e$B("\e(Bj8@h  \e$B("\e(B\E$@    \e$B("\e(B\E(H    \e$B("\e(B--      \e$B("\e(B--      \e$B("\e(B\E(H    \e$B("\e(B                  \e$B("\e(B
+    \e$B("\e(Bjubb  \e$B("\e(B\E$B    \e$B("\e(B\E(B    \e$B("\e(B\E(I    \e$B("\e(B\E(B    \e$B("\e(B\E(B    \e$B("\e(B                  \e$B("\e(B
+    \e$B("\e(Bjubj  \e$B("\e(B\E$B    \e$B("\e(B\E(J    \e$B("\e(B\E(I    \e$B("\e(B\E(J    \e$B("\e(B\E(J    \e$B("\e(Bjunet\e$B$HF1$8\e(B       \e$B("\e(B
+    \e$B("\e(Bjubh  \e$B("\e(B\E$B    \e$B("\e(B\E(H    \e$B("\e(B\E(I    \e$B("\e(B\E(H    \e$B("\e(B\E(H    \e$B("\e(B                  \e$B("\e(B
+    \e$B("\e(Bju@b  \e$B("\e(B\E$@    \e$B("\e(B\E(B    \e$B("\e(B\E(I    \e$B("\e(B\E(B    \e$B("\e(B\E(B    \e$B("\e(B                  \e$B("\e(B
+    \e$B("\e(Bju@j  \e$B("\e(B\E$@    \e$B("\e(B\E(J    \e$B("\e(B\E(I    \e$B("\e(B\E(J    \e$B("\e(B\E(J    \e$B("\e(B                  \e$B("\e(B
+    \e$B("\e(Bju@h  \e$B("\e(B\E$@    \e$B("\e(B\E(H    \e$B("\e(B\E(I    \e$B("\e(B\E(H    \e$B("\e(B\E(H    \e$B("\e(B                  \e$B("\e(B
+    \e$B(&(!(!(!(*(!(!(!(!(*(!(!(!(!(*(!(!(!(!(*(!(!(!(!(*(!(!(!(!(*(!(!(!(!(!(!(!(!(!(%\e(B
+    
+    \e$B$$$:$l$N>l9g$b!"$9$G$KB8:_$7$F$$$kL>A0$KBP$7$F$O!"4A;z$N3+;O=*N;%7!<%1%s%9$O!"0J2<\e(B
+    \e$B$N$b$N$rG'<1$7$^$9!#\e(B
+        \e$B4A;z$N;O$^$j\e(B: \E$B  \e$B$+\e(B  \E$@
+         \e$B4A;z$N=*$j\e(B:  \E(J  \e$B$+\e(B \E(B \e$B$+\e(B \E(H
+    
+(2) smbclient \e$B$N%*%W%7%g%s\e(B
+
+  \e$B%/%i%$%"%s%H%W%m%0%i%`$G$b!"4A;z$d2>L>$r4^$s$@%U%!%$%k$r07$($k$h$&$K!"<!$N%*%W%7%g%s\e(B
+  \e$B$rDI2C$7$^$7$?!#\e(B
+
+    -t <\e$B%?!<%_%J%k%3!<%I7O\e(B>
+
+  \e$B$3$3$G!"\e(B<\e$B%?!<%_%J%k%3!<%I7O\e(B>\e$B$K;XDj$G$-$k$b$N$O!">e$N\e(B<\e$B%3!<%I7O\e(B>\e$B$HF1$8$b$N$G$9!#\e(B
+
+(3) \e$B%G%U%)%k%H\e(B
+
+  \e$B%G%U%)%k%H$N%3!<%I7O$O!"%3%s%Q%$%k;~$K7h$^$j$^$9!#\e(B
+
+3. \e$B%3%s%Q%$%k;~$N@_Dj\e(B
+
+  Makefile \e$B$K@_Dj$9$k9`L\$r0J2<$K<($7$^$9!#\e(B
+
+(1) KANJI \e$B%U%i%0\e(B
+
+  \e$B%3%s%Q%$%k%*%W%7%g%s$K\e(B -DKANJI=\"\e$B%3!<%I7O\e(B\" \e$B$r;XDj$7$^$9!#$3$N%3!<%I7O$O\e(B 2. \e$B$G;X\e(B
+  \e$BDj$9$k$b$N$HF1$8$G$9!#Nc$($P!"\e(B-DKANJI=\"euc\" \e$B$r\e(BFLAGSM \e$B$K@_Dj$9$k$H\e(B UNIX \e$B>e$N%U%!\e(B
+  \e$B%$%kL>$O!"\e(BEUC \e$B%3!<%I$K$J$j$^$9!#$3$3$G;XDj$7$?%3!<%I7O$O!"%5!<%P5Z$S%/%i%$%"%s%H\e(B
+  \e$B%W%m%0%i%`$N%G%U%)%k%H$KCM$J$j$^$9!#\e(B
+
+3. \e$B@)8B;v9`\e(B
+
+(1) \e$B4A;z%3!<%I\e(B
+  smbd \e$B$rF0:n$5$;$k%[%9%H$N\e(B UNIX \e$B$,%5%]!<%H$7$F$$$J$$4A;z%3!<%I$O!"MxMQ$G$-$J$$$3$H$,\e(B
+  \e$B$"$j$^$9!#JQ$JF0:n$r$9$k$h$&$J$i\e(B hex \e$B$N;XDj$r$9$k$N$,NI$$$G$7$g$&!#\e(B
+
+(2) smbclient \e$B%3%^%s%I\e(B
+  \e$B%7%U%H%3!<%I$J$I$N4X78$G!"4A;z$d2>L>$r4^$s$@%U%!%$%kL>$N\e(B ls \e$B$NI=<($,Mp$l$k$3$H$,$"$j\e(B
+  \e$B$^$9!#\e(B
+
+(3) \e$B%o%$%k%I%+!<%I$K$D$$$F\e(B
+  \e$B$A$c$s$H$7$?%9%Z%C%/$,$h$/$o$+$i$J$+$C$?$N$G$9$,!"0l1~!"\e(BDOS/V \e$B$NF0:n$HF1$8F0:n$r9T$J\e(B
+  \e$B$&$h$&$K$J$C$F$$$^$9!#\e(B
+
+4. \e$B>c32Ey$N%l%]!<%H$K$D$$$F\e(B
+
+  \e$BF|K\8l$N%U%!%$%kL>$K4X$7$F!"J8;z2=$1Ey$N>c32$,$"$l$P!";d$K%l%]!<%H$7$FD:$1$l$P9,$$$G\e(B
+\e$B$9!#$?$@$7!"%*%j%8%J%k$+$i$NLdBjE@$d<ALd$K$D$$$F$O!"%*%j%8%J%k$N:n<T$XD>@\Ld$$9g$o$;$k\e(B
+\e$B$+!"$b$7$/$O%a!<%j%s%0%j%9%H$J$I$X%l%]!<%H$9$k$h$&$K$7$F2<$5$$!#\e(B
+
+5. \e$B$=$NB>\e(B
+
+  hex \e$B7A<0$NJQ49J}K!$O!"\e(B
+
+    \e$BBgLZ!wBgDM!&C^GH\e(B <ohki@gssm.otsuka.tsukuba.ac.jp>\e$B;a\e(B
+
+  \e$B$,:n$i$l$?%3!<%I$rMxMQ$7$F$$$^$9!#\e(B
+
+1994\e$BG/\e(B10\e$B7n\e(B28\e$BF|\e(B \e$BBh#1HG\e(B
+1995\e$BG/\e(B 8\e$B7n\e(B16\e$BF|\e(B \e$BBh#2HG\e(B
+\e$BF#ED\e(B \e$B?r\e(B  fujita@ainix.isac.co.jp
+
diff --git a/docs/textdocs/README.sambatar b/docs/textdocs/README.sambatar
new file mode 100644 (file)
index 0000000..2682995
--- /dev/null
@@ -0,0 +1,15 @@
+
+This is version 1.4 of my small extension to samba that allows PC shares
+to be backed up directly to a UNIX tape. It only has been tested under
+Solaris 2.3, Linux 1.1.59 and DG/UX 5.4r3.10 with version 1.9.13 of samba.
+
+See the file INSTALL for installation instructions, and
+the man page and NOTES file for some basic usage. Please let me know if you
+have any problems getting it to work under your flavour of Unix.
+
+This is only (yet another) intermediate version of sambatar.
+This version also comes with an extra gift, zen.bas, written in
+microsoft qbasic by a colleague. It is (apparently) based on a 70s
+British sci-fi series known as Blake's 7. If you have any questions
+about this program, or any suggestions (e.g. what about servillan.bas
+?), feel free to mail the author (of zen.bas) greenm@lilhd.logica.com.
diff --git a/docs/textdocs/SCO.txt b/docs/textdocs/SCO.txt
new file mode 100644 (file)
index 0000000..1b38014
--- /dev/null
@@ -0,0 +1,12 @@
+There is an annoying TCPIP bug in SCO Unix. This causes orruption when
+transferring files with Samba.
+
+Geza Makay (makayg@math.u-szeged.hu) sends this information:
+
+The patch you need is UOD385 Connection Drivers SLS. It is available from
+SCO (ftp.sco.com, directory SLS, files uod385a.Z and uod385a.ltr.Z).
+
+You do not need anything else but the above patch. It installs in seconds,
+and corrected the Excel problem. We also had some other minor problems (not
+only with Samba) that disappeared by installing this patch.
+
diff --git a/docs/textdocs/SMBTAR.notes b/docs/textdocs/SMBTAR.notes
new file mode 100644 (file)
index 0000000..a23cbf2
--- /dev/null
@@ -0,0 +1,40 @@
+
+Intro
+-----
+
+sambatar is just a small extension to the smbclient program distributed with
+samba. A basic front end shell script, smbtar, is provided as an interface
+to the smbclient extensions.
+
+Extensions
+----------
+
+This release adds the following extensions to smbclient,
+
+tar [c|x] filename
+  creates or restores from a tar file. The tar file may be a tape
+or a unix tar file. tar's behaviour is modified with the newer and tarmode
+commands.
+
+tarmode [full|inc|reset|noreset]
+  With no arguments, tarmode prints the current tar mode (by default full,
+noreset). In full mode, every file is backed up during a tar command.
+In incremental, only files with the dos archive bit set are backed up.
+The archive bit is reset if in reset mode, or left untouched if in noreset.
+In reset mode, the share has to be writable, which makes sambatar even
+less secure. An alternative might be to use tarmode inc noreset which
+would implement an "expanding incremental" backup (which some may prefer
+anyway).
+
+setmode <setmode string> filename
+  This is a "freebie" - nothing really to do with sambatar. This 
+is a crude attrib like command (only the other way around). Setmode string
+is a combination of +-rhsa. So for example -rh would reset the read only
+bit on filename.
+
+newer filename
+  This is in fact part of the 1.9.13 samba distribution, but comes
+into its own with sambatar. This causes tar (or get, mget, etc) to
+only copy files newer than the specified file name. Could be used
+against the previous nights (or whatever) log file to implement incremental
+backups.
\ No newline at end of file
diff --git a/docs/textdocs/Speed.txt b/docs/textdocs/Speed.txt
new file mode 100644 (file)
index 0000000..5dfd703
--- /dev/null
@@ -0,0 +1,272 @@
+This file tries to outline the ways to improve the speed of a Samba server.
+
+Andrew Tridgell
+January 1995
+
+
+COMPARISONS
+-----------
+
+The Samba server uses TCP to talk to the client. Thus if you are
+trying to see if it performs well you should really compare it to
+programs that use the same protocol. The most readily available
+programs for file transfer that use TCP are ftp or another TCP based
+SMB server.
+
+If you want to test against something like a NT or WfWg server then
+you will have to disable all but TCP on either the client or
+server. Otherwise you may well be using a totally different protocol
+(such as Netbeui) and comparisons may not be valid.
+
+Generally you should find that Samba performs similarly to ftp at raw
+transfer speed. It should perform quite a bit faster than NFS,
+although this very much depends on your system.
+
+Several people have done comparisons between Samba and Novell, NFS or
+WinNT. In some cases Samba performed the best, in others the worst. I
+suspect the biggest factor is not Samba vs some other system but the
+hardware and drivers used on the various systems. Given similar
+hardware Samba should certainly be competitive in speed with other
+systems.
+
+
+SOCKET OPTIONS
+--------------
+
+There are a number of socket options that can greatly affect the
+performance of a TCP based server like Samba.
+
+The socket options that Samba uses are settable both on the command
+line with the -O option, or in the smb.conf file.
+
+The "socket options" section of the smb.conf manual page describes how
+to set these and gives recommendations.
+
+Getting the socket options right can make a big difference to your
+performance, but getting them wrong can degrade it by just as
+much. The correct settings are very dependent on your local network.
+
+The socket option TCP_NODELAY is the one that seems to make the
+biggest single difference for most networks. Many people report that
+adding "socket options = TCP_NODELAY" doubles the read performance of
+a Samba drive. The best explanation I have seen for this is that the
+Microsoft TCP/IP stack is slow in sending tcp ACKs.
+
+
+READ SIZE
+---------
+
+The option "read size" affects the overlap of disk reads/writes with
+network reads/writes. If the amount of data being transferred in
+several of the SMB commands (currently SMBwrite, SMBwriteX and
+SMBreadbraw) is larger than this value then the server begins writing
+the data before it has received the whole packet from the network, or
+in the case of SMBreadbraw, it begins writing to the network before
+all the data has been read from disk.
+
+This overlapping works best when the speeds of disk and network access
+are similar, having very little effect when the speed of one is much
+greater than the other.
+
+The default value is 16384, but very little experimentation has been
+done yet to determine the optimal value, and it is likely that the best
+value will vary greatly between systems anyway. A value over 65536 is
+pointless and will cause you to allocate memory unnecessarily.
+
+
+MAX XMIT
+--------
+
+At startup the client and server negotiate a "maximum transmit" size,
+which limits the size of nearly all SMB commands. You can set the
+maximum size that Samba will negotiate using the "max xmit = " option
+in smb.conf.
+
+It defaults to 65536 bytes (the maximum), but it is possible that some
+clients may perform better with a smaller transmit unit. Trying values
+of less than 2048 is likely to cause severe problems.
+
+In most cases the default is the best option.
+
+
+LOCKING
+-------
+
+By default Samba does not implement strict locking on each read/write
+call (although it did in previous versions). If you enable strict
+locking (using "strict locking = yes") then you may find that you
+suffer a severe performance hit on some systems.
+
+The performance hit will probably be greater on NFS mounted
+filesystems, but could be quite high even on local disks.
+
+
+SHARE MODES
+-----------
+
+Some people find that opening files is very slow. This is often
+because of the "share modes" code needed to fully implement the dos
+share modes stuff. You can disable this code using "share modes =
+no". This will gain you a lot in opening and closing files but will
+mean that (in some cases) the system won't force a second user of a
+file to open the file read-only if the first has it open
+read-write. For many applications that do their own locking this
+doesn't matter, but for some it may.
+
+LOG LEVEL
+---------
+
+If you set the log level (also known as "debug level") higher than 2
+then you may suffer a large drop in performance. This is because the
+server flushes the log file after each operation, which can be very
+expensive. 
+
+
+WIDE LINKS
+----------
+
+The "wide links" option is now enabled by default, but if you disable
+it (for better security) then you may suffer a performance hit in
+resolving filenames. The performance loss is lessened if you have
+"getwd cache = yes", which is now the default.
+
+
+READ RAW
+--------
+
+The "read raw" operation is designed to be an optimised, low-latency
+file read operation. A server may choose to not support it,
+however. and Samba makes support for "read raw" optional, with it
+being enabled by default.
+
+In some cases clients don't handle "read raw" very well and actually
+get lower performance using it than they get using the conventional
+read operations. 
+
+So you might like to try "read raw = no" and see what happens on your
+network. It might lower, raise or not affect your performance. Only
+testing can really tell.
+
+
+WRITE RAW
+---------
+
+The "write raw" operation is designed to be an optimised, low-latency
+file write operation. A server may choose to not support it,
+however. and Samba makes support for "write raw" optional, with it
+being enabled by default.
+
+Some machines may find "write raw" slower than normal write, in which
+case you may wish to change this option.
+
+READ PREDICTION
+---------------
+
+Samba can do read prediction on some of the SMB commands. Read
+prediction means that Samba reads some extra data on the last file it
+read while waiting for the next SMB command to arrive. It can then
+respond more quickly when the next read request arrives.
+
+This is disabled by default. You can enable it by using "read
+prediction = yes".
+
+Note that read prediction is only used on files that were opened read
+only.
+
+Read prediction should particularly help for those silly clients (such
+as "Write" under NT) which do lots of very small reads on a file.
+
+Samba will not read ahead more data than the amount specified in the
+"read size" option. It always reads ahead on 1k block boundaries.
+
+
+MEMORY MAPPING
+--------------
+
+Samba supports reading files via memory mapping them. One some
+machines this can give a large boost to performance, on others it
+makes not difference at all, and on some it may reduce performance.
+
+To enable you you have to recompile Samba with the -DUSE_MMAP=1 option
+on the FLAGS line of the Makefile.
+
+Note that memory mapping is only used on files opened read only, and
+is not used by the "read raw" operation. Thus you may find memory
+mapping is more effective if you disable "read raw" using "read raw =
+no".
+
+
+SLOW CLIENTS
+------------
+
+One person has reported that setting the protocol to COREPLUS rather
+than LANMAN2 gave a dramatic speed improvement (from 10k/s to 150k/s).
+
+I suspect that his PC's (386sx16 based) were asking for more data than
+they could chew. I suspect a similar speed could be had by setting
+"read raw = no" and "max xmit = 2048", instead of changing the
+protocol. Lowering the "read size" might also help.
+
+
+SLOW LOGINS
+-----------
+
+Slow logins are almost always due to the password checking time. Using
+the lowest practical "password level" will improve things a lot. You
+could also enable the "UFC crypt" option in the Makefile.
+
+CLIENT TUNING
+-------------
+
+Often a speed problem can be traced to the client. The client (for
+example Windows for Workgroups) can often be tuned for better TCP
+performance.
+
+See your client docs for details. In particular, I have heard rumours
+that the WfWg options TCPWINDOWSIZE and TCPSEGMENTSIZE can have a
+large impact on performance.
+
+Also note that some people have found that setting DefaultRcvWindow in
+the [MSTCP] section of the SYSTEM.INI file under WfWg to 3072 gives a
+big improvement. I don't know why.
+
+My own experience wth DefaultRcvWindow is that I get much better
+performance with a large value (16384 or larger). Other people have
+reported that anything over 3072 slows things down enourmously. One
+person even reported a speed drop of a factor of 30 when he went from
+3072 to 8192. I don't know why.
+
+It probably depends a lot on your hardware, and the type of unix box
+you have at the other end of the link.
+
+MY RESULTS
+----------
+
+Some people want to see real numbers in a document like this, so here
+they are. I have a 486sx33 client running WfWg 3.11 with the 3.11b
+tcp/ip stack. It has a slow IDE drive and 20Mb of ram. It has a SMC
+Elite-16 ISA bus ethernet card. The only WfWg tuning I've done is to
+set DefaultRcvWindow in the [MSTCP] section of system.ini to 16384. My
+server is a 486dx3-66 running Linux. It also has 20Mb of ram and a SMC
+Elite-16 card. You can see my server config in the examples/tridge/
+subdirectory of the distribution.
+
+I get 490k/s on reading a 8Mb file with copy.
+I get 441k/s writing the same file to the samba server.
+
+Of course, there's a lot more to benchmarks than 2 raw throughput
+figures, but it gives you a ballpark figure.
+
+I've also tested Win95 and WinNT, and found WinNT gave me the best
+speed as a samba client. The fastest client of all (for me) is
+smbclient running on another linux box. Maybe I'll add those results
+here someday ...
+
+
+COMMENTS
+--------
+
+If you've read this far then please give me some feedback! Which of
+the above suggestions worked for you?
+
+Mail the samba mailing list or samba-bugs@anu.edu.au
diff --git a/docs/textdocs/Support.txt b/docs/textdocs/Support.txt
new file mode 100644 (file)
index 0000000..d71bdaf
--- /dev/null
@@ -0,0 +1,376 @@
+The Samba Consultants List
+==========================
+
+This is a list of people who are prepared to install and support Samba.
+Note that in most countries nobody should admit to "supplying" Samba, since 
+there is then an implied warranty with possibly onerous legal obligations. 
+Just downloading and installing it isn't supply in this sense, but advertising 
+"run our Samba for best results" may be so.
+
+Being on this list does not imply any sort of endorsement by anyone, it is just
+provided in the hope that it will be useful.
+
+If you want to be added to the list, or want your entry modified then
+contact the address below. They are currently listed in the
+order that they were received. If it gets too big we may organise it
+by region. Please make sure to include a header line giving the region
+and country, eg CANBERRA  - AUSTRALIA. 
+
+You can contact the maintainers at samba-bugs@anu.edu.au
+
+
+------------------------------------------------------------------------------
+BRISBANE - AUSTRALIA
+
+Brett Worth
+Select Computer Technology - Brisbane
+431 Logan Road
+Stones Corner   QLD   4120
+E-Mail: brett@sct.com.au
+------------------------------------------------------------------------------
+
+------------------------------------------------------------------------------
+CANBERRA - AUSTRALIA
+
+Paul Blackman (ictinus@lake.canberra.edu.au, Ph. 06 2012518) is
+available for consultation.  Paul's Samba background is with
+Solaris 2.3/4 and WFWG/Win95 machines.  Paul is also the maintainer
+of the SAMBA Web Pages.
+------------------------------------------------------------------------------
+
+------------------------------------------------------------------------------
+READING - ENGLAND
+
+Philip Hands                  |   E-Mail: info@hands.com
+Philip Hands Computing Ltd.   |   Tel: +44 1734 476287 Fax: 1734 474655
+Unit 1, Cherry Close, Caversham, Reading RG4 8UP  UK
+
+Samba experience:  SVR4,SVR3.2 & Linux <--> WfWg, W3.1, OS2 and MS-LanMan
+------------------------------------------------------------------------------
+
+------------------------------------------------------------------------------
+ILLONOIS - USA
+
+Information One, Inc.
+736 Hinman Ave, Suite 2W
+Evanston, IL 60202
+708-328-9137  708-328-0117 FAX  
+info@info1.com
+
+Providing custom Internet and networking solutions.
+------------------------------------------------------------------------------
+
+------------------------------------------------------------------------------
+Olympic Peninsula Consulting; 1241 Lansing Ave W., Bremerton, WA 98312-4343
+telephone 1+ 360 792 6938; mailto:opc@aa.net; http://www.aa.net/~opc;
+Unix Systems and TCP/IP Network design, programming, and administration.
+------------------------------------------------------------------------------
+
+------------------------------------------------------------------------------
+SolutionS R Us has been in business for 3+ years providing viable 3rd
+party support in system/network administration. With our own Linux
+distribution which we're constantly improving to make it the best and
+using it to provide total solutions for companies which are open to
+using Linux.
+
+Mauro DePalma  <mauro@sru.com>
+------------------------------------------------------------------------------
+
+------------------------------------------------------------------------------
+BIELEFELD - GERMANY
+
+I am located in Bielefeld/Germany and have been doing Unix consultancy
+work for the past 8 years throughout Germany and the rest of Europe. I
+can be contacted by email at <jpm@mens.de> or via phone at +49 521
+9225922 or telefax at +49 521 9225924.
+------------------------------------------------------------------------------
+
+------------------------------------------------------------------------------
+CANBERRA - AUSTRALIA
+
+Ben Elliston
+Faculty of Information Sciences and Engineering
+University of Canberra AUSTRALIA
+E-mail: ben@ise.canberra.edu.au (Uni)
+------------------------------------------------------------------------------
+
+------------------------------------------------------------------------------
+PALERMO - ITALY
+
+Francesco Cardinale
+E-Mail: cardinal@palermo.italtel.it
+Samba experience: SVR3.2, SOLARIS, ULTRIX, LINUX <--> DOS LAN-MAN, WFW
+------------------------------------------------------------------------------
+
+------------------------------------------------------------------------------
+SYDNEY - AUSTRALIA
+
+John Terpstra - Aquasoft (jht@aquasoft.com.au)
+Business:  +612 524 4040
+Home:      +612 540 3154
+Shoephone: +612 414 334422  (aka 0414 334422)
+------------------------------------------------------------------------------
+
+------------------------------------------------------------------------------
+ONTARIO - CANADA
+
+Strata Software Limited, Kanata Ontario CANADA
+Tel:   +1 (613) 591-1922   Fax:   +1 (613) 591-3485
+Email: sales@strataware.com   WWW: http://www.strataware.com/
+
+Strata Software Limited is a software development and consulting group
+specializing in data communications (TCP/IP and OSI), X.400, X.500 and
+LDAP, and X.509-based security.  We have Samba experience with Windows NT,
+Windows 95, and Windows for Workgroups clients with Linux, Unixware
+(SVR4), and HP-UX servers.
+------------------------------------------------------------------------------
+
+-----------------------------------------------------------------------
+SYDNEY - AUSTRALIA
+
+We are a Unix & Windows developer with a consulting & support component.
+In business since 1981 with experience on Sun, hp, sgi, IBM rs6000 plus
+Windows, NT and Win95,  Using Samba since September 94.
+CodeSmiths,  22 Darley Road, MANLY 2095  NSW;  977 1979; fax: 977 2116
+philm@esi.com.au   (Australia; New South Wales; SYDNEY; North East)
+-----------------------------------------------------------------------
+
+------------------------------------------------------------------------------
+EDINBUGH - SCOTLAND
+
+Charlie Hussey                            email charlie@edina.demon.co.uk
+Edina Software Limited                    tel   0131 657 1129
+4 James Street                            fax   0131 669 9092
+Edinburgh EH15 2DS
+
+SAMBA experience: SCO UNIX  <=> WfWg
+------------------------------------------------------------------------------
+
+------------------------------------------------------------------------------
+LONDON - ENGLAND
+
+Mark H. Preston,
+Network Analyst,                    | Email : mpreston@sghms.ac.uk
+Computer Unit,                      | Tel   : +44 (0)181 725-5434
+St. George's Hospital Med School,   | Fax   : +44 (0)181 725-3583
+London SW17 ORE.                    | WWW   : http://www.sghms.ac.uk
+
+Samba Experience:
+Server: Solaris 2.3 & 2.4, Irix 5.2 & 5.3
+Client: WinNT, Win95, WfWg, Win3.1, Ms-LanMan, DHCP support
+------------------------------------------------------------------------------
+
+------------------------------------------------------------------------------
+SYDNEY - AUSTRALIA
+
+Pacific ESI has used and installed Samba since 1.6 on a range
+of machines running SunOS, BSD/OS, SCO/UNIX, HP/UX, and Solaris,
+and WfWG and Windows95.  The largest system worked on to date
+involved an Australia wide network of machines with PCs and SUNs
+at the various nodes.  The in-house testing site is a wide area
+network with three sites, remotely connected with PPP and with
+SUN servers at each site to all of which are connected several
+PCs running mainly WfWG.
+
+Stefan Kjellberg                Pacific Engineering Systems
+International
+info@eram.esi.com.au            Voice:+61-2-9063377
+... Fax:+61-2-9063468
+------------------------------------------------------------------------------
+
+------------------------------------------------------------------------------
+CHANTILLY - USA
+
+Intelligent Decisions, Inc.
+ATTN: Richard Bullington
+14121 Parke Long Ct. #104
+Chantilly, VA 22021
+U.S.A.
+(703) 803-8070
+rbullington@intdec.com
+
+Samba experience: Linux, DEC ULTRIX <=> WFWG 3.11, Windows NT 3.5
+Specializing in World Wide Web related UNIX-to-PC connectivity.
+------------------------------------------------------------------------------
+
+------------------------------------------------------------------------------
+FORT COLLINS, CO - USA
+
+Granite Computing Solutions
+ATTN: Brian Grossman
+Box 270103
+Fort Collins, CO  80527-0103
+U.S.A.
+(970) 225-2370
+granite@fortnet.org
+
+Information services, including WfWG, NT, Apple <=> Unix interoperability.
+
+Our standard advertisement says:
+
+>               Unix workstations, servers and custom systems           <
+>>              WWW and Unix education                                   <<
+>>>             Enterprise and departmental computing solutions           <<<
+>>>             Backup & restore                                          <<<
+>>              Software forensics                                       <<
+>               Data translation                                        <
+------------------------------------------------------------------------------
+
+----------------------------------------------------------
+Adelaide, Australia
+
+NS Computer Software and Services P/L
+PO Box 86
+Ingle Farm
+SA 5098
+
+Contact: Richard Sharpe
+        Ph: +61-8-281-0063 (08-281-0063) AH
+        FAX:+61-8-250-2080 (08-250-2080)
+
+Experience with: ULTRIX, Digital UNIX, SunOS, WfW 3.11, Win95, WNT 3.51
+
+----------------------------------------------------------
+
+----------------------------------------------------------
+TECTONIC LIMITED
+WESTWOOD
+78 LOUGHBOROUGH ROAD
+QUORN
+LEICESTERSHIRE
+LE12 8DX
+
+TELEPHONE 01509-620922
+FAX       01509-620933
+
+CONTACT DAVID ROBINSON
+
+WE ARE UNIX ORIENTATED BUT ALSO SPECIALISE IN PC TO UNIX COMMUNICATIONS, WE 
+KNOW AND UNDERSTAND PC-NFS, (HENCE OUR INTEREST IN SAMBA).
+WE SUPPORT  SUNOS, SOLARIS 1.X AND 2.X, HP-UX 9.0 AND 10.0, OSF (or DEC UNIX, 
+whichever you prefer), WinNT, WfWG and Win95.
+
+WE ARE ALREADY TALKING TO A COUPLE OF VERY LARGE SAMBA USERS HERE IN THE UK. 
+WE WOULD LIKE TO SUPPORT THEM (AND MANY MORE), WOULD YOU PLEASE CONTACT ME ON:
+david@tectonic.demon.co.uk
+----------------------------------------------------------
+
+----------------------------------------------------------
+MIAMI, FL - USA
+
+Swaney & Associates, Inc.
+ATTN:  Stephen Swaney
+       2543 Lincoln Avenue
+       Miami, Florida 33133
+       U.S.A
+       (305) 860-0570
+
+Specializing in:
+       High Availability system & networks
+       UNIX to PC connectivity
+       Market Data systems
+       Messaging Systems (Sendmail & Microsoft Exchange)
+----------------------------------------------------------
+
+------------------------------------------------------------------------------
+NEW JERSEY  - USA
+
+William J. Maggio                                       
+LAN & Computer Integrators, Inc.
+242 Old New Brunswick Road                              Email: bmaggio@lci.com
+Suite 440                                               Voice: 908-981-1991
+Piscataway, NJ 08855                                    Fax  : 908-981-1858
+
+   Specializing in Internet connectivity and security, Sun integration and 
+   high speed, enterprise network design and deployment.
+------------------------------------------------------------------------------
+
+FAREHAM - ENGLAND
+
+High Field Technology Ltd
+Little Park Farm Road, Segensworth West,
+Fareham, Hants PO15 5SJ, UK.
+sales@hft.co.uk        tel +44 148 957 0111 fax +44 148 957 0555
+
+Company skills: Real time hardware and software systems
+
+Samba experience:  BSD/OS, Linux, LynxOS <==> WFWG, NT                     
+
+------------------------------------------------------------------------------
+
+-----------------------------------------------------------------------
+QUEBEC - CANADA
+
+Dataden Computer Systems
+Attn: Danny Arseneau
+arseneau@parkmed.com
+895 2nd Avenue
+Ile Bizard, Quebec
+Canada, H9C 1K3
+Tel: (514)891-2293
+Fax: (514)696-0848
+
+Dataden is company that specializes in Unix--TCP/IP networking.
+We have over 15 years of experience.  We have been installing,
+configuring and maintaining Samba for clients for 1-1/2 years now.  We
+have samba installations on Linx, SunOS and DEC OSF.  Our biggest site
+has 4 Suns and 3 Linux servers running Samba which are serving a network
+of about 50 PC's running WFWg and Win95.
+-----------------------------------------------------------------------
+
+-----------------------------------------------------------------------
+CALIFORNIA - USA
+
+Ron Halstead
+Open Systems Consulting
+3098-4 Lakemont Drive
+San Ramon, CA 94583 (San Francisco Bay Area)
+(510) 735-7529
+halstead@ix.netcom.com
+-----------------------------------------------------------------------
+
+
+-----------------------------------------------------------------------
+MELBOURNE - AUSTRALIA
+
+Michael Ciavarella
+Cybersoruce Pty Ltd.
+8/140 Queen Street
+Melbourne  VIC 3000
+Phone:  +61-3-9642-5997
+Fax:    +61-3-9642-5998
+Email:  mikec@cyber.com.au
+WWW:    http://www.cyber.com.au
+
+Cybersource specialises in TCP/IP network integration and Open Systems
+administration.  Cybersource is an Australian-owned and operated 
+company, with clients including some of Australia's largest financial,
+petrochemical and state government organisations.  
+-----------------------------------------------------------------------
+
+-----------------------------------------------------------------------
+SOUTHERN CALIFORNIA - USA
+
+Michael St. Laurent
+Serving Los Angeles and Orange Counties.  Please contact via email.
+rowl@earthlink.net
+Michael St. Laurent
+Hartwell Corporation
+------------------------------------------------------------------------------
+WASHINGTON DC METRO - USA
+
+Asset Software, Inc. has been running Samba since the 1.6 release on various
+platforms, including SunOS 4.x, Solaris 2.x, IRIX 4.x and 5.x, Linux 1.1x,
+1.2x, and 1.3x, and BSD UNIX 4.3 and above.  We specialize in small office
+network solutions and provide services to enhance a small office's
+operations.  Primarily a custom software operation, our vast knowledge of
+Windows, DOS, Unix, Windows NT, MacOS, and OS/2 enable us to provide quality
+technical assistance to the small office environment at a reasonable price.
+Our upcoming multi-mailbox mail client, IQ Mail, enables users with more
+than one mailbox to send and retrieve their mail from a single, consistent
+mail client running in Windows.
+
+David J. Fenwick                                          Asset Software, Inc.
+President                                                      djf@assetsw.com
+------------------------------------------------------------------------------
+
diff --git a/docs/textdocs/UNIX-SMB.txt b/docs/textdocs/UNIX-SMB.txt
new file mode 100644 (file)
index 0000000..b2c0642
--- /dev/null
@@ -0,0 +1,220 @@
+This is a short document that describes some of the issues that
+confront a SMB implementation on unix, and how Samba copes with
+them. They may help people who are looking at unix<->PC
+interoperability.
+
+It was written to help out a person who was writing a paper on unix to
+PC connectivity.
+
+Andrew Tridgell
+April 1995
+
+
+Usernames
+=========
+
+The SMB protocol has only a loose username concept. Early SMB
+protocols (such as CORE and COREPLUS) have no username concept at
+all. Even in later protocols clients often attempt operations
+(particularly printer operations) without first validating a username
+on the server.
+
+Unix security is based around username/password pairs. A unix box
+should not allow clients to do any substantive operation without some
+sort of validation. 
+
+The problem mostly manifests itself when the unix server is in "share
+level" security mode. This is the default mode as the alternative
+"user level" security mode usually forces a client to connect to the
+server as the same user for each connected share, which is
+inconvenient in many sites.
+
+In "share level" security the client normally gives a username in the
+"session setup" protocol, but does not supply an accompanying
+password. The client then connects to resources using the "tree
+connect" protocol, and supplies a password. The problem is that the
+user on the PC types the username and the password in different
+contexts, unaware that they need to go together to give access to the
+server. The username is normally the one the user typed in when they
+"logged onto" the PC (this assumes Windows for Workgroups). The
+password is the one they chose when connecting to the disk or printer.
+
+The user often chooses a totally different username for their login as
+for the drive connection. Often they also want to access different
+drives as different usernames. The unix server needs some way of
+divining the correct username to combine with each password.
+
+Samba tries to avoid this problem using several methods. These succeed
+in the vast majority of cases. The methods include username maps, the
+service%user syntax, the saving of session setup usernames for later
+validation and the derivation of the username from the service name
+(either directly or via the user= option).
+
+File Ownership
+==============
+
+The commonly used SMB protocols have no way of saying "you can't do
+that because you don't own the file". They have, in fact, no concept
+of file ownership at all.
+
+This brings up all sorts of interesting problems. For example, when
+you copy a file to a unix drive, and the file is world writeable but
+owned by another user the file will transfer correctly but will
+receive the wrong date. This is because the utime() call under unix
+only succeeds for the owner of the file, or root, even if the file is
+world writeable. For security reasons Samba does all file operations
+as the validated user, not root, so the utime() fails. This can stuff
+up shared development diectories as programs like "make" will not get
+file time comparisons right.
+
+There are several possible solutions to this problem, including
+username mapping, and forcing a specific username for particular
+shares.
+
+Passwords
+=========
+
+Many SMB clients uppercase passwords before sending them. I have no
+idea why they do this. Interestingly WfWg uppercases the password only
+if the server is running a protocol greater than COREPLUS, so
+obviously it isn't just the data entry routines that are to blame.
+
+Unix passwords are case sensitive. So if users use mixed case
+passwords they are in trouble.
+
+Samba can try to cope with this by either using the "password level"
+option which causes Samba to try the offered password with up to the
+specified number of case changes, or by using the "password server"
+option which allows Samba to do it's validation via another machine
+(typically a WinNT server).
+
+Samba also doesn't support the password encryption method used by SMB
+clients. This is because the spec isn't sufficiently detailed for an
+implementation (although Jeremy Allison is working on it, to try and
+work it out). Also, there is a fundamental problem with what we
+understand so far in the algorithm, as it seems that the server would
+need to store somewhere on disk a reversibly encrypted (effectively
+plaintext) copy of the users password in order to use the
+algorithm. This goes against the unix policy that "even the super-user
+doesn't know your password" which comes from the use of a one-way hash
+function.
+
+Locking
+=======
+
+The locking calls available under a DOS/Windows environment are much
+richer than those available in unix. This means a unix server (like
+Samba) choosing to use the standard fcntl() based unix locking calls
+to implement SMB locking has to improvise a bit.
+
+One major problem is that dos locks can be in a 32 bit (unsigned)
+range. Unix locking calls are 32 bits, but are signed, giving only a 31
+bit range. Unfortunately OLE2 clients use the top bit to select a
+locking range used for OLE semaphores.
+
+To work around this problem Samba compresses the 32 bit range into 31
+bits by appropriate bit shifting. This seems to work but is not
+ideal. In a future version a separate SMB lockd may be added to cope
+with the problem.
+
+It also doesn't help that many unix lockd daemons are very buggy and
+crash at the slightest provocation. They normally go mostly unused in
+a unix environment because few unix programs use byte range
+locking. The stress of huge numbers of lock requests from dos/windows
+clients can kill the daemon on some systems.
+
+The second major problem is the "opportunistic locking" requested by
+some clients. If a client requests opportunistic locking then it is
+asking the server to notify it if anyone else tries to do something on
+the same file, at which time the client will say if it is willing to
+give up it's lock. Unix has no simple way of implementing
+opportunistic locking, and currently Samba has no support for it.
+
+Deny Modes
+==========
+
+When a SMB client opens a file it asks for a particular "deny mode" to
+be placed on the file. These modes (DENY_NONE, DENY_READ, DENY_WRITE,
+DENY_ALL, DENY_FCB and DENY_DOS) specify what actions should be
+allowed by anyone else who tries to use the file at the same time. If
+DENY_READ is placed on the file, for example, then any attempt to open
+the file for reading should fail.
+
+Unix has no equivalent notion. To implement these Samba uses lock
+files based on the files inode and placed in a separate lock
+directory. These are clumsy and consume processing and file resources,
+so they are optional and off by default.
+
+Trapdoor UIDs
+=============
+
+A SMB session can run with several uids on the one socket. This
+happens when a user connects to two shares with different
+usernames. To cope with this the unix server needs to switch uids
+within the one process. On some unixes (such as SCO) this is not
+possible. This means that on those unixes the client is restricted to
+a single uid.
+
+Port numbers
+============
+
+There is a convention that clients on sockets use high "unprivilaged"
+port numbers (>1000) and connect to servers on low "privilaged" port
+numbers. This is enforced in Unix as non-root users can't open a
+socket for listening on port numbers less than 1000.
+
+Most PC based SMB clients (such as WfWg and WinNT) don't follow this
+convention completely. The main culprit is the netbios nameserving on
+udp port 137. Name query requests come from a source port of 137. This
+is a problem when you combine it with the common firewalling technique
+of not allowing incoming packets on low port numbers. This means that
+these clients can't query a netbios nameserver on the other side of a
+low port based firewall.
+
+The problem is more severe with netbios node status queries. I've
+found that WfWg, Win95 and WinNT3.5 all respond to netbios node status
+queries on port 137 no matter what the source port was in the
+request. This works between machines that are both using port 137, but
+it means it's not possible for a unix user to do a node status request
+to any of these OSes unless they are running as root. The answer comes
+back, but it goes to port 137 which the unix user can't listen
+on. Interestingly WinNT3.1 got this right - it sends node status
+responses back to the source port in the request.
+
+
+Protocol Complexity
+===================
+
+There are many "protocol levels" in the SMB protocol. It seems that
+each time new functionality was added to a Microsoft operating system,
+they added the equivalent functions in a new protocol level of the SMB
+protocol to "externalise" the new capabilities.
+
+This means the protocol is very "rich", offering many ways of doing
+each file operation. This means SMB servers need to be complex and
+large. It also means it is very difficult to make them bug free. It is
+not just Samba that suffers from this problem, other servers such as
+WinNT don't support every variation of every call and it has almost
+certainly been a headache for MS developers to support the myriad of
+SMB calls that are available.
+
+There are about 65 "top level" operations in the SMB protocol (things
+like SMBread and SMBwrite). Some of these include hundreds of
+sub-functions (SMBtrans has at least 120 sub-functions, like
+DosPrintQAdd and NetSessionEnum). All of them take several options
+that can change the way they work. Many take dozens of possible
+"information levels" that change the structures that need to be
+returned. Samba supports all but 2 of the "top level" functions. It
+supports only 8 (so far) of the SMBtrans sub-functions. Even NT
+doesn't support them all.
+
+Samba currently supports up to the "NT LM 0.12" protocol, which is the
+one preferred by Win95 and WinNT3.5. Luckily this protocol level has a
+"capabilities" field which specifies which super-duper new-fangled
+options the server suports. This helps to make the implementation of
+this protocol level much easier.
+
+There is also a problem with the SMB specications. SMB is a X/Open
+spec, but the X/Open book is far from ideal, and fails to cover many
+important issues, leaving much to the imagination.
+
diff --git a/docs/textdocs/WinNT.txt b/docs/textdocs/WinNT.txt
new file mode 100644 (file)
index 0000000..b57abb7
--- /dev/null
@@ -0,0 +1,56 @@
+There are some particular issues with Samba and Windows NT
+
+=====================================================================
+One of the most annoying problems with WinNT is that NT refuses to
+connect to a server that is in user level security mode and that
+doesn't support password encryption unless it first prompts the user
+for a password.
+
+This means even if you have the same password on the NT box and the
+Samba server you will get prompted for a password. Entering the
+correct password will get you connected.
+
+The other major ramification of this feature of NT is that it can't
+browse a user level non-encrypted server unless it already has a
+connection open. This is because there is no spot for a password
+prompt in the browser window. It works fine if you already have a
+drive mounted (for example, one auto mounted on startup).
+
+Samba should support encrypted passwords soon, which will solve this
+problem.
+=====================================================================
+
+
+
+=====================================================================
+When you mount a printer using the print manager in NT you may find
+the following info from Matthew Harrell <harrell@leech.nrl.navy.mil>
+useful:
+
+------------
+        I noticed in your change-log you noted that some people were
+still unable to use print manager under NT.  If this is the same problem
+that I encountered, it's caused by the length of time it takes NT to
+determine if the printer is ready.
+
+The problem occurs when you double-click on a printer to connect it to
+the NT machine.  Because it's unable to determine if the printer is ready
+in the short span of time it has, it assumes it isn't and gives some
+strange error about not having enough resources (I forget what the error
+is).  A solution to this that seems to work fine for us is to click
+once on the printer, look at the bottom of the window and wait until
+it says it's ready, then clilck on "OK".
+
+By the way, this problem probably occurs in our group because the
+Samba server doesn't actually have the printers - it queues them to
+remote printers either on other machines or using their own network
+cards.  Because of this "middle layer", it takes an extra amount of
+time for the NT machine to get verification that the printer queue
+actually exists.
+
+I hope this helped in some way...
+-----------
+=====================================================================
+
+
+
diff --git a/examples/README b/examples/README
new file mode 100644 (file)
index 0000000..3e58e0f
--- /dev/null
@@ -0,0 +1,6 @@
+This directory contains example config files for Samba. If you have an
+interesting config file, then please send it in for inclusion in the
+package.
+
+Send it to: Andrew.Tridgell@anu.edu.au
+
diff --git a/examples/dce-dfs/README b/examples/dce-dfs/README
new file mode 100644 (file)
index 0000000..4aaba8b
--- /dev/null
@@ -0,0 +1,4 @@
+this is a sample configuration file from Jim Doyle <doyle@oec.com> who
+did the DCE/DFS patches for Samba. It shows how to make DCE/DFS shares
+available. 
+
diff --git a/examples/dce-dfs/smb.conf b/examples/dce-dfs/smb.conf
new file mode 100644 (file)
index 0000000..f5f155b
--- /dev/null
@@ -0,0 +1,42 @@
+[global]
+   printing = bsd
+   printcap name = /etc/printcap
+   load printers = no
+   guest account = guest
+   log file = /usr/local/samba/var/log.%m
+   log level = 8
+   password level = 8
+
+[homes]
+   comment = Home Directories
+   browseable = no
+   read only = no
+   create mode = 0750
+
+[test]
+   comment = test stuff
+   path = /dept/mis/home/testacct
+   valid users = testacct
+   public = no
+   writable = yes
+
+[namespace]
+   comment = DCE-DFS Global Root 
+   path = /...
+   public = no
+   writable = yes
+
+[oecdfs]
+   comment = Corporate Cell
+   path = /.../corp.boston.oec.com/fs
+   browseable = no
+   read only = no
+   create mode = 0750
+
+[develdfs]
+   comment = Technology Development Cell
+   path = /.../devel.boston.oec.com/fs
+   browseable = no
+   read only = no
+   create mode = 0750
+
diff --git a/examples/misc/extra_smbstatus b/examples/misc/extra_smbstatus
new file mode 100644 (file)
index 0000000..b018f3d
--- /dev/null
@@ -0,0 +1,47 @@
+Here's something that Paul Blackman sent me that may be useful:
+
+-------------------
+I created this script to do a few things that smbstatus doesn't at the
+moment.  Perhaps you might want to include these.  Sorry I haven't
+added things at source level, script was quick&easy.
+
+*******
+#!/bin/csh
+if ($1 == "-p") then
+  smbstatus -p |sort -u
+else if ($1 == "-c") then
+  echo There are `smbstatus -p |sort -u |grep -n -v z |grep -c :` unique
+smbd processes running.
+ else if ($1 == "-l") then
+  echo `date '+ %d/%m/%y %H:%M:%S'` `smbstatus -p |sort -u |grep -n -v z
+|grep -c :` >>$2
+else
+  smbstatus |sort +3 -4 -u
+endif
+******
+
+The '-p' option was just to show unique PIDs.
+
+The more important ones are the '-c' and '-l' options '-c' just counts
+the number of unique smbd's, While '-l' logs this count with date and
+time to a log file specified on the command line.  I'm using '-l' at
+the moment with cron to give me an idea of usage/max connections etc.
+I was also thinking of doing a log for individual/specified services.
+
+The default (last) option was to show unique PIDs with user names.
+Unfortunately this still lists all file locks etc.  This would be
+better with a 'no locked files' option from smbstatus (or is there one
+that I didn't see)
+
+Cheers,
+~^ MIME OK ^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~
+    o        |  Paul Blackman                  ictinus@lake.canberra.edu.au
+        o    |  Co-operative Research              ------------------------
+   o  _      |  Centre For Freshwater Ecology.        Ph.  (Aus) 06 2012518
+  -- (") o   |  University of Canberra, Australia.       Fax. "  06 2015038
+    \_|_--   |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+      |      |  "Spend a little love and get high"
+    _/ \_    |                                              - Lenny Kravitz
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~ SAMBA Web Pages: http://samba.canberra.edu.au/pub/samba/samba.html ~~~~~
+
diff --git a/examples/misc/wall.perl b/examples/misc/wall.perl
new file mode 100644 (file)
index 0000000..fc3dc2e
--- /dev/null
@@ -0,0 +1,45 @@
+#!/usr/local/bin/perl
+# 
+#@(#) smb-wall.pl Description:
+#@(#) A perl script which allows you to announce whatever you choose to
+#@(#) every PC client currently connected to a Samba Server...
+#@(#) ...using "smbclient -M" message to winpopup service.
+#@(#) Default usage is to message every connected PC.
+#@(#) Alternate usage is to message every pc on the argument list.
+#@(#)  Hacked up by Keith Farrar <farrar@parc.xerox.com>
+#
+#=============================================================================
+$smbstatus = "/usr/local/bin/smbstatus";
+$smbclient = "/usr/local/bin/smbclient";
+
+print STDOUT "\nEnter message for Samba clients of this host\n";
+print STDOUT "(terminated with single '.' or end of file):\n";
+
+while ( <STDIN> ) {
+       /^\.$/ && last;
+       push(@message,  $_);
+}
+
+if ( $ARGV[0] ne "" ) {
+       $debug && print STDOUT "Was given args: \n\t @ARGV\n";
+       foreach $client ( @ARGV ) {
+               $pcclient{$client} = $client;
+       }
+} else {
+       open( PCLIST, "$smbstatus | /bin/awk '/^[a-z]/ {print $5}' | /bin/sort | /bin/uniq|");
+       while ( <PCLIST> ) {
+               /^[a-z]+[a-z0-9A-Z-_]+.+/ || next;
+               ($share, $user, $group, $pid, $client, @junk) = split;
+               $pcclient{$client} = $client;
+       }
+       close(PCLIST);
+}
+
+foreach $pc ( keys(%pcclient) ) {
+       print STDOUT "Sending message ";
+       $debug && print STDOUT " <@message> \n";
+       print STDOUT "To <$pc>\n";
+       open(SENDMSG,"|$smbclient -M $pc") || next;
+       print SENDMSG @message;
+       close(SENDMSG);
+}
diff --git a/examples/printing/smbprint b/examples/printing/smbprint
new file mode 100755 (executable)
index 0000000..a80d60c
--- /dev/null
@@ -0,0 +1,77 @@
+#!/bin/sh -x
+
+# This script is an input filter for printcap printing on a unix machine. It
+# uses the smbclient program to print the file to the specified smb-based 
+# server and service.
+# For example you could have a printcap entry like this
+#
+# smb:lp=/dev/null:sd=/usr/spool/smb:sh:if=/usr/local/samba/smbprint
+#
+# which would create a unix printer called "smb" that will print via this 
+# script. You will need to create the spool directory /usr/spool/smb with
+# appropriate permissions and ownerships for your system.
+
+# Set these to the server and service you wish to print to 
+# In this example I have a WfWg PC called "lapland" that has a printer 
+# exported called "printer" with no password.
+
+#
+# Script further altered by hamiltom@ecnz.co.nz (Michael Hamilton)
+# so that the server, service, and password can be read from 
+# a /usr/var/spool/lpd/PRINTNAME/.config file.
+#
+# In order for this to work the /etc/printcap entry must include an 
+# accounting file (af=...):
+#
+#   cdcolour:\
+#      :cm=CD IBM Colorjet on 6th:\
+#      :sd=/var/spool/lpd/cdcolour:\
+#      :af=/var/spool/lpd/cdcolour/acct:\
+#      :if=/usr/local/etc/smbprint:\
+#      :mx=0:\
+#      :lp=/dev/null:
+#
+# The /usr/var/spool/lpd/PRINTNAME/.config file should contain:
+#   server=PC_SERVER
+#   service=PR_SHARENAME
+#   password="password"
+#
+# E.g.
+#   server=PAULS_PC
+#   service=CJET_371
+#   password=""
+
+#
+# Debugging log file, change to /dev/null if you like.
+#
+logfile=/tmp/smb-print.log
+# logfile=/dev/null
+
+
+#
+# The last parameter to the filter is the accounting file name.
+#   Extract the directory name from the file name.
+#   Concat this with /.config to get the config file.
+#
+eval acct_file=\$$#
+spool_dir=`dirname $acct_file` 
+config_file=$spool_dir/.config
+
+# Should read the following variables set in the config file:
+#   server
+#   service
+#   password
+eval `cat $config_file`
+
+#
+# Some debugging help, change the >> to > if you want to same space.
+#
+echo "server $server, service $service" >> $logfile
+
+(
+# NOTE You may wish to add the line `echo translate' if you want automatic
+# CR/LF translation when printing.
+#       echo translate
+       echo "print -"
+       cat
+) | /usr/local/samba/bin/smbclient "\\\\$server\\$service" $password -U $server -N -P >> $logfile
diff --git a/examples/printing/smbprint.sysv b/examples/printing/smbprint.sysv
new file mode 100644 (file)
index 0000000..3e1cec4
--- /dev/null
@@ -0,0 +1,52 @@
+#!/bin/sh
+#
+# @(#) smbprint.sysv version 1.0 Ross Wakelin <r.wakelin@march.co.uk>
+#
+#      Version 1.0 13 January 1995
+#              modified from the original smbprint (bsd) script
+#
+# this script is a System 5 printer interface script. It 
+# uses the smbclient program to print the file to the specified smb-based 
+# server and service. 
+# 
+# To add this to your lp system, copy this file into your samba directory 
+# (the example here is /opt/samba), modify the server and service variables 
+# and then execute the following command (as root) 
+#
+# lpadmin -punixprintername -v/dev/null -i/opt/samba/smbprint 
+# 
+# where        unixprintername is the name that the printer will be known as 
+# on your unix box. 
+# 
+# the script smbprint will be copied into your printer administration 
+# directory (/usr/lib/lp or /etc/lp) as a new interface 
+# (interface/unixprintername)
+# Then you have to enable unixprintername and accept unixprintername
+#
+# This script will then be called by the lp service to print the files
+# This script will have 6 or more parameters passed to it by the lp service.
+# The first five will contain details of the print job, who queued it etc,
+# while parameters 6 onwards are a list of files to print.  We just
+# cat these at the samba client.
+#
+# Set these to the server and service you wish to print to 
+# In this example I have a WfWg PC called "lapland" that has a printer 
+# exported called "printer" with no password.
+#
+# clear out the unwanted parameters
+shift;shift;shift;shift;shift
+# now the argument list is just the files to print
+
+server=admin
+service=hplj2
+password=""
+
+(
+# NOTE You may wish to add the line `echo translate' if you want automatic
+# CR/LF translation when printing.
+       echo translate
+       echo "print -"
+       cat $*
+) | /opt/samba/smbclient "\\\\$server\\$service" $password -N -P  > /dev/null
+exit $?
+
diff --git a/examples/simple/README b/examples/simple/README
new file mode 100644 (file)
index 0000000..9628aa8
--- /dev/null
@@ -0,0 +1,2 @@
+This is the "original" sample config file.
+
diff --git a/examples/simple/smb.conf b/examples/simple/smb.conf
new file mode 100644 (file)
index 0000000..cdf65b3
--- /dev/null
@@ -0,0 +1,165 @@
+; Configuration file for smbd.
+; ============================================================================
+; For the format of this file and comprehensive descriptions of all the
+; configuration option, please refer to the man page for smb.conf(5).
+;
+; The following configuration should suit most systems for basic usage and 
+; initial testing. It gives all clients access to their home directories and
+; allows access to all printers specified in /etc/printcap.
+;
+; Things you need to check:
+; --------------------------
+;
+; 1: Check the path to your printcap file. If you are using a system that does 
+;    not use printcap (eg., Solaris), create a file containing lines of the 
+;    form
+;
+;       printername|printername|printername|
+;
+;    where each "printername" is the name of a printer you want to provide 
+;    access to. Then alter the "printcap =" entry to point to the new file.
+;
+;    If using Solaris, the following command will generate a suitable printcap
+;    file:
+;
+;       lpc status | grep ":" | sed s/:/\|/ > myprintcap
+;
+; 2: Make sure the "print command" entry is correct for your system. This 
+;    command should submit a file (represented by %s) to a printer 
+;    (represented by %p) for printing and should REMOVE the file after 
+;    printing.
+;  
+;    One most systems the default will be OK, as long as you get "printing ="
+;    right.
+;    
+;    It is also a good idea to use an absolute path in the print command
+;    as there is no guarantee the search path will be set correctly.
+;
+; 3: Make sure the "printing =" option is set correctly for your system.
+;    Possible values are "sysv", "bsd" or "aix".
+;
+; 4: Make sure the "lpq command" entry is correct for your system. The default
+;    may not work for you.
+;
+; 5: Make sure that the user specified in "guest account" exists. Typically
+;    this will be a user that cannot log in and has minimal privileges.
+;    Often the "nobody" account doesn't work (very system dependant).
+;
+; 6: You should consider the "security =" option. See a full description
+;    in the main documentation and the smb.conf(5) manual page
+;
+; 7: Look at the "hosts allow" option, unless you want everyone on the internet
+;    to be able to access your files.
+;
+[global]
+   printing = bsd
+   printcap name = /etc/printcap
+   load printers = yes
+   guest account = pcguest
+;  This next option sets a separate log file for each client. Remove
+;  it if you want a combined log file.
+   log file = /usr/local/samba/log.%m
+
+;  You will need a world readable lock directory and "share modes=yes"
+;  if you want to support the file sharing modes for multiple users
+;  of the same files
+;  lock directory = /usr/local/samba/var/locks
+;  share modes = yes
+
+[homes]
+   comment = Home Directories
+   browseable = no
+   read only = no
+   create mode = 0750
+
+[printers]
+   comment = All Printers
+   browseable = no
+   printable = yes
+   public = no
+   writable = no
+   create mode = 0700
+
+; you might also want this one
+; [tmp]
+;   comment = Temporary file space
+;   path = /tmp
+;   read only = no
+;   public = yes
+
+;
+; Other examples. 
+;
+; A private printer, usable only by fred. Spool data will be placed in fred's
+; home directory. Note that fred must have write access to the spool directory,
+; wherever it is.
+;[fredsprn]
+;   comment = Fred's Printer
+;   valid users = fred
+;   path = /homes/fred
+;   printer = freds_printer
+;   public = no
+;   writable = no
+;   printable = yes
+;
+; A private directory, usable only by fred. Note that fred requires write
+; access to the directory.
+;[fredsdir]
+;   comment = Fred's Service
+;   path = /usr/somewhere/private
+;   valid users = fred
+;   public = no
+;   writable = yes
+;   printable = no
+;
+; A publicly accessible directory, but read only, except for people in
+; the staff group
+;[public]
+;   comment = Public Stuff
+;   path = /usr/somewhere/public
+;   public = yes
+;   writable = no
+;   printable = no
+;   write list = @staff
+;
+; a service which has a different directory for each machine that connects
+; this allows you to tailor configurations to incoming machines. You could
+; also use the %u option to tailor it by user name.
+; The %m gets replaced with the machine name that is connecting.
+;[pchome]
+;  comment = PC Directories
+;  path = /usr/pc/%m
+;  public = no
+;  writeable = yes
+;
+;
+; A publicly accessible directory, read/write to all users. Note that all files
+; created in the directory by users will be owned by the default user, so
+; any user with access can delete any other user's files. Obviously this
+; directory must be writable by the default user. Another user could of course
+; be specified, in which case all files would be owned by that user instead.
+;[public]
+;   path = /usr/somewhere/else/public
+;   public = yes
+;   only guest = yes
+;   writable = yes
+;   printable = no
+;
+;
+; The following two entries demonstrate how to share a directory so that two
+; users can place files there that will be owned by the specific users. In this
+; setup, the directory should be writable by both users and should have the
+; sticky bit set on it to prevent abuse. Obviously this could be extended to
+; as many users as required.
+;[myshare]
+;   comment = Mary's and Fred's stuff
+;   path = /usr/somewhere/shared
+;   valid users = mary fred
+;   public = no
+;   writable = yes
+;   printable = no
+;   create mask = 0765
+
+
+
+
diff --git a/examples/tridge/README b/examples/tridge/README
new file mode 100644 (file)
index 0000000..11c72f2
--- /dev/null
@@ -0,0 +1,8 @@
+This is the configuration I use at home. I have 2 client PCs, one
+running Win95, one running alternately WfWg and NTAS3.5. My server is
+a 486dx2-66 Linux box.
+
+Note that I use the %a and %m macros to load smb.conf extensions
+particular to machines and architectures. This gives me a lot of
+flexibility in how I handle each of the machines.
+
diff --git a/examples/tridge/smb.conf b/examples/tridge/smb.conf
new file mode 100644 (file)
index 0000000..a2f269f
--- /dev/null
@@ -0,0 +1,101 @@
+[global]   
+   config file = /usr/local/samba/smb.conf.%m
+   status = yes
+   security = user
+   encrypt passwords = yes
+   server string = Tridge (%v,%h)
+   load printers = yes
+   log level = 1
+   log file = /usr/local/samba/var/log.%m
+   guest account = pcguest
+   hosts allow = 192.0.2. localhost 
+   password level = 2
+   auto services = tridge susan
+   message command = csh -c '/usr/bin/X11/xedit -display :0 %s;rm %s' &
+   read prediction = yes
+   socket options = TCP_NODELAY 
+   valid chars = Ã¶:֠å:Ã… Ã¤:Ä 
+   share modes = yes
+   locking = yes
+   strict locking = yes
+   keepalive = 30
+   include = /usr/local/samba/lib/smb.conf.%m
+   include = /usr/local/samba/lib/smb.conf.%a
+
+
+[uniprint]
+   comment = University Printing
+   path = /home/susan/print
+   user = susan
+   postscript = yes
+   print ok = yes
+   print command = xmenu -heading "%s" OK&
+
+[testprn]
+   comment = Test printer
+   path = /tmp
+   user = susan
+   print ok = yes
+   print command = cp %s /tmp/smb.%U.prn
+   lpq command = cat /tmp/xxyz
+
+[amd]
+   comment = amd area
+   path = /mount
+   force user = tridge
+   read only = no
+
+[homes]
+   browseable = no
+   guest ok = no
+   read only = no
+   create mask = 0755
+
+[printers]
+  browseable = no
+  comment = Printer in Printcap
+  guest ok = no
+  path = /tmp
+  read only = no
+  print ok = yes
+
+[dos]
+  browseable = yes
+  comment = Dos Files
+  force group = samba
+  create mode = 0775
+  path = /home/tridge/dos
+  copy = homes
+
+[msoffice]
+  browseable = yes
+  comment = Microsoft Office
+  force group = samba
+  create mode = 0775
+  path = /data/msoffice
+  read only = yes
+
+[root]
+  comment = Root Dir
+  copy = dos
+  path = /
+  dont descend = /proc ./proc /etc
+
+[tmp]
+  comment = tmp files
+  copy = dos
+  path = /tmp
+  read only = no
+
+
+[cdrom]
+  comment = Tridge's CdRom
+  path = /mount/cdrom
+  read only = yes
+  locking = no
+
+[data]
+       comment = Data Partition
+       path = /data
+       read only = yes
+       guest ok = yes
diff --git a/examples/tridge/smb.conf.WinNT b/examples/tridge/smb.conf.WinNT
new file mode 100644 (file)
index 0000000..f490f83
--- /dev/null
@@ -0,0 +1,14 @@
+#log level = 4
+#readraw = no
+#writeraw = no
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/tridge/smb.conf.fjall b/examples/tridge/smb.conf.fjall
new file mode 100644 (file)
index 0000000..76f4d0e
--- /dev/null
@@ -0,0 +1,21 @@
+;log level = 4
+;readraw = no
+;writeraw = no
+;password level = 4
+;mangled map = (;1 )
+;protocol = lanman1
+;user = susan
+;getwd cache = no
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/tridge/smb.conf.lapland b/examples/tridge/smb.conf.lapland
new file mode 100644 (file)
index 0000000..f490f83
--- /dev/null
@@ -0,0 +1,14 @@
+#log level = 4
+#readraw = no
+#writeraw = no
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/tridge/smb.conf.vittjokk b/examples/tridge/smb.conf.vittjokk
new file mode 100644 (file)
index 0000000..919ecd1
--- /dev/null
@@ -0,0 +1,14 @@
+;protocol = LANMAN2
+log level = 2
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/source/change-log b/source/change-log
new file mode 100644 (file)
index 0000000..e120ac6
--- /dev/null
@@ -0,0 +1,1872 @@
+Change Log for Samba
+
+Unless otherwise attributed, all changes were made by 
+Andrew.Tridgell@anu.edu.au
+
+NOTE: THIS LOG IS IN CHRONOLOGICAL ORDER
+
+
+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
+       
+       
+==========
+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
+       
\ No newline at end of file
diff --git a/source/client/client.c b/source/client/client.c
new file mode 100644 (file)
index 0000000..504cb5a
--- /dev/null
@@ -0,0 +1,4534 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   SMB client
+   Copyright (C) Andrew Tridgell 1994-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.
+*/
+
+#ifdef SYSLOG
+#undef SYSLOG
+#endif
+
+#include "includes.h"
+#include "nameserv.h"
+
+#ifndef REGISTER
+#define REGISTER 0
+#endif
+
+pstring cur_dir = "\\";
+pstring cd_path = "";
+pstring service="";
+pstring desthost="";
+pstring myname = "";
+pstring password = "";
+pstring username="";
+pstring workgroup=WORKGROUP;
+BOOL got_pass = False;
+BOOL connect_as_printer = False;
+BOOL connect_as_ipc = False;
+extern struct in_addr bcast_ip;
+static BOOL got_bcast=False;
+
+char cryptkey[8];
+BOOL doencrypt=False;
+
+extern pstring user_socket_options;
+
+/* 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)
+
+int name_type = 0x20;
+
+int max_protocol = PROTOCOL_NT1;
+
+
+time_t newer_than = 0;
+int archive_level = 0;
+
+extern struct in_addr myip;
+
+extern pstring debugf;
+extern int DEBUGLEVEL;
+
+BOOL translation = False;
+
+/* clitar bits insert */
+extern void cmd_tar();
+extern void cmd_block();
+extern void cmd_tarmode();
+extern void cmd_setmode();
+extern int blocksize;
+extern BOOL tar_inc;
+extern BOOL tar_reset;
+extern int process_tar();
+extern int tar_parseargs();
+/* clitar bits end */
+
+int cnum = 0;
+int pid = 0;
+int gid = 0;
+int uid = 0;
+int mid = 0;
+int myumask = 0755;
+
+int max_xmit = BUFFER_SIZE;
+
+extern pstring scope;
+
+BOOL prompt = True;
+
+int printmode = 1;
+
+BOOL recurse = False;
+BOOL lowercase = False;
+
+BOOL have_ip = False;
+
+struct in_addr dest_ip;
+
+#define SEPARATORS " \t\n\r"
+
+BOOL abort_mget = True;
+
+extern int Protocol;
+
+BOOL readbraw_supported = False;
+BOOL writebraw_supported = False;
+
+pstring fileselection = "";
+
+extern file_info def_finfo;
+
+/* timing globals */
+int get_total_size = 0;
+int get_total_time_ms = 0;
+int put_total_size = 0;
+int put_total_time_ms = 0;
+
+
+extern int Client;
+
+#define USENMB
+
+#ifdef KANJI
+extern int coding_system;
+#define CNV_LANG(s) (coding_system == DOSV_CODE?s:dos_to_unix(s, False))
+#define CNV_INPUT(s) (coding_system == DOSV_CODE?s:unix_to_dos(s, True))
+static BOOL
+setup_term_code (char *code)
+{
+    int new;
+    new = interpret_coding_system (code, UNKNOWN_CODE);
+    if (new != UNKNOWN_CODE) {
+       coding_system = new;
+       return True;
+    }
+    return False;
+}
+#else
+#define CNV_LANG(s) dos2unix_format(s,False)
+#define CNV_INPUT(s) unix2dos_format(s,True)
+#endif
+
+static void send_logout(void );
+BOOL reopen_connection(char *inbuf,char *outbuf);
+static int do_long_dir(char *inbuf,char *outbuf,char *Mask,int attribute,void (*fn)(),BOOL recurse_dir);
+static int do_short_dir(char *inbuf,char *outbuf,char *Mask,int attribute,void (*fn)(),BOOL recurse_dir);
+static BOOL call_api(int prcnt,int drcnt,int mprcnt,int mdrcnt,
+                    int *rprcnt,int *rdrcnt,char *param,char *data,
+                    char **rparam,char **rdata);
+static BOOL send_trans_request(char *outbuf,int trans,
+                              char *name,int fid,int flags,
+                              char *data,char *param,uint16 *setup,
+                              int ldata,int lparam,int lsetup,
+                              int mdata,int mparam,int msetup);
+
+
+/****************************************************************************
+setup basics in a outgoing packet
+****************************************************************************/
+void setup_pkt(char *outbuf)
+{
+  SSVAL(outbuf,smb_pid,pid);
+  SSVAL(outbuf,smb_uid,uid);
+  SSVAL(outbuf,smb_mid,mid);
+  if (Protocol > PROTOCOL_CORE)
+    {
+      SCVAL(outbuf,smb_flg,0x8);
+      SSVAL(outbuf,smb_flg2,0x1);
+    }
+}
+
+/****************************************************************************
+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 size, int n, FILE *f)
+{
+  int i;
+  int c;
+
+  if (!translation || (size != 1))
+    return(fread(b,size,n,f));
+  
+  i = 0;
+  while (i < n)
+    {
+      if ((c = getc(f)) == EOF)
+       {
+         break;
+       }
+      
+      if (c == '\n') /* change all LFs to CR/LF */
+       {
+         b[i++] = '\r';
+         n++;
+       }
+      
+      b[i++] = c;
+    }
+  
+  return(i);
+}
+
+/****************************************************************************
+read from a file with print translation. return the number read. read approx n
+bytes.
+****************************************************************************/
+static int printread(FILE *f,char *b,int n)
+{
+  int i;
+
+  i = readfile(b,1, n-1,f);
+#if FORMFEED
+  if (feof(f) && i>0)
+    b[i++] = '\014';
+#endif
+
+  return(i);
+}
+
+/****************************************************************************
+check for existance of a dir
+****************************************************************************/
+static BOOL chkpath(char *path,BOOL report)
+{
+  fstring path2;
+  pstring inbuf,outbuf;
+  char *p;
+
+  strcpy(path2,path);
+  trim_string(path2,NULL,"\\");
+  if (!*path2) *path2 = '\\';
+
+  bzero(outbuf,smb_size);
+  set_message(outbuf,0,4 + strlen(path2),True);
+  SCVAL(outbuf,smb_com,SMBchkpth);
+  SSVAL(outbuf,smb_tid,cnum);
+  setup_pkt(outbuf);
+
+  p = smb_buf(outbuf);
+  *p++ = 4;
+  strcpy(p,path2);
+
+  send_smb(Client,outbuf);
+  receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+  if (report && CVAL(inbuf,smb_rcls) != 0)
+    DEBUG(2,("chkpath: %s\n",smb_errstr(inbuf)));
+
+  return(CVAL(inbuf,smb_rcls) == 0);
+}
+
+
+/****************************************************************************
+send a message
+****************************************************************************/
+static void send_message(char *inbuf,char *outbuf)
+{
+  int total_len = 0;
+
+  char *p;
+  int grp_id;
+
+  /* send a SMBsendstrt command */
+  bzero(outbuf,smb_size);
+  set_message(outbuf,0,0,True);
+  CVAL(outbuf,smb_com) = SMBsendstrt;
+  SSVAL(outbuf,smb_tid,cnum);
+
+  p = smb_buf(outbuf);
+  *p++ = 4;
+  strcpy(p,username);
+  p = skip_string(p,1);
+  *p++ = 4;
+  strcpy(p,desthost);
+  p = skip_string(p,1);
+
+  set_message(outbuf,0,PTR_DIFF(p,smb_buf(outbuf)),False);
+
+  send_smb(Client,outbuf);
+  
+
+  if (!receive_smb(Client,inbuf,SHORT_TIMEOUT) || CVAL(inbuf,smb_rcls) != 0)
+    {
+      printf("SMBsendstrt failed. (%s)\n",smb_errstr(inbuf));
+      return;
+    }
+
+  grp_id = SVAL(inbuf,smb_vwv0);
+
+  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;
+
+      bzero(msg,smb_size);
+
+      for (l=0;l<maxlen && (c=fgetc(stdin))!=EOF;l++)
+       {
+         if (c == '\n')
+           msg[l++] = '\r';
+         msg[l] = c;   
+       }
+
+      CVAL(outbuf,smb_com) = SMBsendtxt;
+
+      set_message(outbuf,1,l+3,True);
+
+      SSVAL(outbuf,smb_vwv0,grp_id);
+
+      p = smb_buf(outbuf);
+      *p = 1;
+      SSVAL(p,1,l);
+      memcpy(p+3,msg,l);
+
+      send_smb(Client,outbuf);
+      
+
+      if (!receive_smb(Client,inbuf,SHORT_TIMEOUT) || CVAL(inbuf,smb_rcls) != 0)
+       {
+         printf("SMBsendtxt failed (%s)\n",smb_errstr(inbuf));
+         return;
+       }      
+
+      total_len += l;
+    }
+
+  if (total_len >= 1600)
+    printf("the message was truncated to 1600 bytes ");
+  else
+    printf("sent %d bytes ",total_len);
+
+  printf("(status was %d-%d)\n",CVAL(inbuf,smb_rcls),SVAL(inbuf,smb_err));
+
+  CVAL(outbuf,smb_com) = SMBsendend;
+  set_message(outbuf,1,0,False);
+  SSVAL(outbuf,smb_vwv0,grp_id);
+
+  send_smb(Client,outbuf);
+  
+
+  if (!receive_smb(Client,inbuf,SHORT_TIMEOUT) || CVAL(inbuf,smb_rcls) != 0)
+    {
+      printf("SMBsendend failed (%s)\n",smb_errstr(inbuf));
+      return;
+    }      
+}
+
+
+
+/****************************************************************************
+check the space on a device
+****************************************************************************/
+static void do_dskattr(void)
+{
+  pstring inbuf,outbuf;
+
+  bzero(outbuf,smb_size);
+  set_message(outbuf,0,0,True);
+  CVAL(outbuf,smb_com) = SMBdskattr;
+  SSVAL(outbuf,smb_tid,cnum);
+  setup_pkt(outbuf);
+
+  send_smb(Client,outbuf);
+  receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+  if (CVAL(inbuf,smb_rcls) != 0) 
+    DEBUG(0,("Error in dskattr: %s\n",smb_errstr(inbuf)));      
+
+  DEBUG(0,("\n\t\t%d blocks of size %d. %d blocks available\n",
+       SVAL(inbuf,smb_vwv0),
+       SVAL(inbuf,smb_vwv1)*SVAL(inbuf,smb_vwv2),
+       SVAL(inbuf,smb_vwv3)));
+}
+
+/****************************************************************************
+show cd/pwd
+****************************************************************************/
+static void cmd_pwd(void)
+{
+  DEBUG(0,("Current directory is %s",CNV_LANG(service)));
+  DEBUG(0,("%s\n",CNV_LANG(cur_dir)));
+}
+
+
+/****************************************************************************
+change directory - inner section
+****************************************************************************/
+static void do_cd(char *newdir)
+{
+  char *p = newdir;
+  pstring saved_dir;
+  pstring dname;
+      
+  /* Save the current directory in case the
+     new directory is invalid */
+  strcpy(saved_dir, cur_dir);
+  if (*p == '\\')
+    strcpy(cur_dir,p);
+  else
+    strcat(cur_dir,p);
+  if (*(cur_dir+strlen(cur_dir)-1) != '\\') {
+    strcat(cur_dir, "\\");
+  }
+  dos_clean_name(cur_dir);
+  strcpy(dname,cur_dir);
+  strcat(cur_dir,"\\");
+  dos_clean_name(cur_dir);
+
+  if (!strequal(cur_dir,"\\"))
+    if (!chkpath(dname,True))
+      strcpy(cur_dir,saved_dir);
+
+  strcpy(cd_path,cur_dir);
+}
+
+/****************************************************************************
+change directory
+****************************************************************************/
+static void cmd_cd(char *inbuf,char *outbuf)
+{
+  fstring buf;
+
+  if (next_token(NULL,buf,NULL))
+    do_cd(buf);
+  else
+    DEBUG(0,("Current directory is %s\n",CNV_LANG(cur_dir)));
+}
+
+
+/****************************************************************************
+  display info about a file
+  ****************************************************************************/
+static void display_finfo(file_info *finfo)
+{
+  time_t t = finfo->mtime; /* the time is assumed to be passed as GMT */
+  DEBUG(0,("  %-30s%7.7s%10d  %s",
+          CNV_LANG(finfo->name),
+          attrib_string(finfo->mode),
+          finfo->size,
+          asctime(LocalTime(&t,GMT_TO_LOCAL))));
+}
+
+/****************************************************************************
+  do a directory listing, calling fn on each file found
+  ****************************************************************************/
+void do_dir(char *inbuf,char *outbuf,char *Mask,int attribute,void (*fn)(),BOOL recurse_dir)
+{
+  DEBUG(5,("do_dir(%s,%x,%s)\n",Mask,attribute,BOOLSTR(recurse_dir)));
+  if (Protocol >= PROTOCOL_LANMAN2)
+    {
+      if (do_long_dir(inbuf,outbuf,Mask,attribute,fn,recurse_dir) > 0)
+       return;
+    }
+
+  expand_mask(Mask,False);
+  do_short_dir(inbuf,outbuf,Mask,attribute,fn,recurse_dir);
+  return;
+}
+
+/*******************************************************************
+  decide if a file should be operated on
+  ********************************************************************/
+static BOOL do_this_one(file_info *finfo)
+{
+  if (finfo->mode & aDIR) return(True);
+
+  if (newer_than && finfo->mtime < newer_than)
+    return(False);
+
+  if ((archive_level==1 || archive_level==2) && !(finfo->mode & aARCH))
+    return(False);
+
+  return(True);
+}
+
+/****************************************************************************
+interpret a short filename structure
+The length of the structure is returned
+****************************************************************************/
+static int interpret_short_filename(char *p,file_info *finfo)
+{
+  finfo->mode = CVAL(p,21);
+
+  /* this date is converted to GMT by make_unix_date */
+  finfo->ctime = make_unix_date(p+22);
+  finfo->mtime = finfo->atime = finfo->ctime;
+  finfo->size = IVAL(p,26);
+  strcpy(finfo->name,p+30);
+  
+  return(DIR_STRUCT_SIZE);
+}
+
+/****************************************************************************
+interpret a long filename structure - this is mostly guesses at the moment
+The length of the structure is returned
+The structure of a long filename depends on the info level. 260 is used
+by NT and 2 is used by OS/2
+****************************************************************************/
+static int interpret_long_filename(int level,char *p,file_info *finfo)
+{
+  if (finfo)
+    memcpy(finfo,&def_finfo,sizeof(*finfo));
+
+  switch (level)
+    {
+    case 1: /* OS/2 understands this */
+      if (finfo)
+       {
+         /* these dates are converted to GMT by make_unix_date */
+         finfo->ctime = make_unix_date2(p+4);
+         finfo->atime = make_unix_date2(p+8);
+         finfo->mtime = make_unix_date2(p+12);
+         finfo->size = IVAL(p,16);
+         finfo->mode = CVAL(p,24);
+         strcpy(finfo->name,p+27);
+       }
+      return(28 + CVAL(p,26));
+
+    case 2: /* this is what OS/2 uses mostly */
+      if (finfo)
+       {
+         /* these dates are converted to GMT by make_unix_date */
+         finfo->ctime = make_unix_date2(p+4);
+         finfo->atime = make_unix_date2(p+8);
+         finfo->mtime = make_unix_date2(p+12);
+         finfo->size = IVAL(p,16);
+         finfo->mode = CVAL(p,24);
+         strcpy(finfo->name,p+31);
+       }
+      return(32 + CVAL(p,30));
+
+      /* levels 3 and 4 are untested */
+    case 3:
+      if (finfo)
+       {
+         /* these dates are probably like the other ones */
+         finfo->ctime = make_unix_date2(p+8);
+         finfo->atime = make_unix_date2(p+12);
+         finfo->mtime = make_unix_date2(p+16);
+         finfo->size = IVAL(p,20);
+         finfo->mode = CVAL(p,28);
+         strcpy(finfo->name,p+33);
+       }
+      return(SVAL(p,4)+4);
+
+    case 4:
+      if (finfo)
+       {
+         /* these dates are probably like the other ones */
+         finfo->ctime = make_unix_date2(p+8);
+         finfo->atime = make_unix_date2(p+12);
+         finfo->mtime = make_unix_date2(p+16);
+         finfo->size = IVAL(p,20);
+         finfo->mode = CVAL(p,28);
+         strcpy(finfo->name,p+37);
+       }
+      return(SVAL(p,4)+4);
+
+    case 260: /* NT uses this, but also accepts 2 */
+      if (finfo)
+       {
+         int ret = SVAL(p,0);
+         int namelen;
+         p += 4; /* next entry offset */
+         p += 4; /* fileindex */
+
+         /* these dates appear to arrive in a weird way. It seems to
+            be localtime plus the serverzone given in the initial
+            connect. This is GMT when DST is not in effect and one
+            hour from GMT otherwise. Can this really be right??
+
+            I suppose this could be called kludge-GMT. Is is the GMT
+            you get by using the current DST setting on a different
+            localtime. It will be cheap to calculate, I suppose, as
+            no DST tables will be needed */
+
+         finfo->ctime = interpret_long_date(p); p += 8;
+         finfo->atime = interpret_long_date(p); p += 8;
+         finfo->mtime = interpret_long_date(p); p += 8; p += 8;
+         finfo->size = IVAL(p,0); p += 8;
+         p += 8; /* alloc size */
+         finfo->mode = CVAL(p,0); p += 4;
+         namelen = IVAL(p,0); p += 4;
+         p += 4; /* EA size */
+         p += 2; /* short name len? */
+         p += 24; /* short name? */      
+         StrnCpy(finfo->name,p,namelen);
+         return(ret);
+       }
+      return(SVAL(p,0));
+    }
+
+  DEBUG(1,("Unknown long filename format %d\n",level));
+  return(SVAL(p,0));
+}
+
+
+
+
+/****************************************************************************
+  act on the files in a dir listing
+  ****************************************************************************/
+static void dir_action(char *inbuf,char *outbuf,int attribute,file_info *finfo,BOOL recurse_dir,void (*fn)(),BOOL longdir)
+{
+
+  if (!((finfo->mode & aDIR) == 0 && *fileselection && 
+       !mask_match(finfo->name,fileselection,False,False)) &&
+      !(recurse_dir && (strequal(finfo->name,".") || 
+                       strequal(finfo->name,".."))))
+    {
+      if (recurse_dir && (finfo->mode & aDIR))
+       {
+         pstring mask2;
+         pstring sav_dir;
+         strcpy(sav_dir,cur_dir);
+         strcat(cur_dir,finfo->name);
+         strcat(cur_dir,"\\");
+         strcpy(mask2,cur_dir);
+
+         if (!fn)
+           DEBUG(0,("\n%s\n",CNV_LANG(cur_dir)));
+
+         strcat(mask2,"*");
+
+         if (longdir)
+           do_long_dir(inbuf,outbuf,mask2,attribute,fn,True);      
+         else
+           do_dir(inbuf,outbuf,mask2,attribute,fn,True);
+
+         strcpy(cur_dir,sav_dir);
+       }
+      else
+       {
+         if (fn && do_this_one(finfo))
+           fn(finfo);
+       }
+    }
+}
+
+
+/****************************************************************************
+  do a directory listing, calling fn on each file found
+  ****************************************************************************/
+static int do_short_dir(char *inbuf,char *outbuf,char *Mask,int attribute,void (*fn)(),BOOL recurse_dir)
+{
+  char *p;
+  int received = 0;
+  BOOL first = True;
+  char status[21];
+  int num_asked = (max_xmit - 100)/DIR_STRUCT_SIZE;
+  int num_received = 0;
+  int i;
+  char *dirlist = NULL;
+  pstring mask;
+  file_info finfo;
+
+  finfo = def_finfo;
+
+  bzero(status,21);
+
+  strcpy(mask,Mask);
+  
+  while (1)
+    {
+      bzero(outbuf,smb_size);
+      if (first)       
+       set_message(outbuf,2,5 + strlen(mask),True);
+      else
+       set_message(outbuf,2,5 + 21,True);
+
+#if FFIRST
+      if (Protocol >= PROTOCOL_LANMAN1)
+       CVAL(outbuf,smb_com) = SMBffirst;
+      else
+#endif
+       CVAL(outbuf,smb_com) = SMBsearch;
+
+      SSVAL(outbuf,smb_tid,cnum);
+      setup_pkt(outbuf);
+
+      SSVAL(outbuf,smb_vwv0,num_asked);
+      SSVAL(outbuf,smb_vwv1,attribute);
+  
+      p = smb_buf(outbuf);
+      *p++ = 4;
+      
+      if (first)
+       strcpy(p,mask);
+      else
+       strcpy(p,"");
+      p += strlen(p) + 1;
+      
+      *p++ = 5;
+      if (first)
+       SSVAL(p,0,0);
+      else
+       {
+         SSVAL(p,0,21);
+         p += 2;
+         memcpy(p,status,21);
+       }
+
+      send_smb(Client,outbuf);
+      receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+      received = SVAL(inbuf,smb_vwv0);
+
+      DEBUG(5,("dir received %d\n",received));
+
+      DEBUG(6,("errstr=%s\n",smb_errstr(inbuf)));
+
+      if (received <= 0) break;
+
+      first = False;
+
+      dirlist = Realloc(dirlist,(num_received + received)*DIR_STRUCT_SIZE);
+
+      if (!dirlist) 
+       return 0;
+
+      p = smb_buf(inbuf) + 3;
+
+      memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
+            p,received*DIR_STRUCT_SIZE);
+
+      memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
+
+      num_received += received;
+
+      if (CVAL(inbuf,smb_rcls) != 0) break;
+    }
+
+#if FFIRST
+  if (!first && Protocol >= PROTOCOL_LANMAN1)
+    {
+      bzero(outbuf,smb_size);
+      CVAL(outbuf,smb_com) = SMBfclose;
+
+      SSVAL(outbuf,smb_tid,cnum);
+      setup_pkt(outbuf);
+
+      p = smb_buf(outbuf);
+      *p++ = 4;
+      
+      strcpy(p,"");
+      p += strlen(p) + 1;
+      
+      *p++ = 5;
+      SSVAL(p,0,21);
+      p += 2;
+      memcpy(p,status,21);
+
+      send_smb(Client,outbuf);
+      receive_smb(Client,inbuf,CLIENT_TIMEOUT,False);
+
+      if (CVAL(inbuf,smb_rcls) != 0) 
+       DEBUG(0,("Error closing search: %s\n",smb_errstr(inbuf)));      
+    }
+#endif
+
+  if (!fn)
+    for (p=dirlist,i=0;i<num_received;i++)
+      {
+       p += interpret_short_filename(p,&finfo);
+       display_finfo(&finfo);
+      }
+
+  for (p=dirlist,i=0;i<num_received;i++)
+    {
+      p += interpret_short_filename(p,&finfo);
+      dir_action(inbuf,outbuf,attribute,&finfo,recurse_dir,fn,False);
+    }
+
+  if (dirlist) free(dirlist);
+  return(num_received);
+}
+
+/****************************************************************************
+  receive a SMB trans or trans2 response allocating the necessary memory
+  ****************************************************************************/
+static BOOL receive_trans_response(char *inbuf,int trans,
+                                   int *data_len,int *param_len,
+                                     char **data,char **param)
+{
+  int total_data=0;
+  int total_param=0;
+  int this_data,this_param;
+
+  *data_len = *param_len = 0;
+
+  receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+  show_msg(inbuf);
+
+  /* sanity check */
+  if (CVAL(inbuf,smb_com) != trans)
+    {
+      DEBUG(0,("Expected %s response, got command 0x%02x\n",
+              trans==SMBtrans?"SMBtrans":"SMBtrans2", CVAL(inbuf,smb_com)));
+      return(False);
+    }
+  if (CVAL(inbuf,smb_rcls) != 0)
+    return(False);
+
+  /* parse out the lengths */
+  total_data = SVAL(inbuf,smb_tdrcnt);
+  total_param = SVAL(inbuf,smb_tprcnt);
+
+  /* allocate it */
+  *data = Realloc(*data,total_data);
+  *param = Realloc(*param,total_param);
+
+  while (1)
+    {
+      this_data = SVAL(inbuf,smb_drcnt);
+      this_param = SVAL(inbuf,smb_prcnt);
+      if (this_data)
+       memcpy(*data + SVAL(inbuf,smb_drdisp),
+              smb_base(inbuf) + SVAL(inbuf,smb_droff),
+              this_data);
+      if (this_param)
+       memcpy(*param + SVAL(inbuf,smb_prdisp),
+              smb_base(inbuf) + SVAL(inbuf,smb_proff),
+              this_param);
+      *data_len += this_data;
+      *param_len += this_param;
+
+      /* parse out the total lengths again - they can shrink! */
+      total_data = SVAL(inbuf,smb_tdrcnt);
+      total_param = SVAL(inbuf,smb_tprcnt);
+
+      if (total_data <= *data_len && total_param <= *param_len)
+       break;
+
+      receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+      show_msg(inbuf);
+
+      /* sanity check */
+      if (CVAL(inbuf,smb_com) != trans)
+       {
+         DEBUG(0,("Expected %s response, got command 0x%02x\n",
+                  trans==SMBtrans?"SMBtrans":"SMBtrans2", CVAL(inbuf,smb_com)));
+         return(False);
+       }
+      if (CVAL(inbuf,smb_rcls) != 0)
+         return(False);
+    }
+  
+  return(True);
+}
+
+/****************************************************************************
+  do a directory listing, calling fn on each file found. Use the TRANSACT2
+  call for long filenames
+  ****************************************************************************/
+static int do_long_dir(char *inbuf,char *outbuf,char *Mask,int attribute,void (*fn)(),BOOL recurse_dir)
+{
+  int max_matches = 512;
+  int info_level = Protocol<PROTOCOL_NT1?1:260; /* NT uses 260, OS/2 uses 2. Both accept 1. */
+  char *p;
+  pstring mask;
+  file_info finfo;
+  int i;
+  char *dirlist = NULL;
+  int dirlist_len = 0;
+  int total_received = 0;
+  BOOL First = True;
+  char *resp_data=NULL;
+  char *resp_param=NULL;
+  int resp_data_len = 0;
+  int resp_param_len=0;
+
+  int ff_resume_key = 0;
+  int ff_searchcount=0;
+  int ff_eos=0;
+  int ff_lastname=0;
+  int ff_dir_handle=0;
+  int loop_count = 0;
+
+  uint16 setup;
+  pstring param;
+
+  strcpy(mask,Mask);
+
+  while (ff_eos == 0)
+    {
+      loop_count++;
+      if (loop_count > 200)
+       {
+         DEBUG(0,("ERROR: Looping in FIND_NEXT??\n"));
+         break;
+       }
+
+      if (First)
+       {
+         setup = TRANSACT2_FINDFIRST;
+         SSVAL(param,0,attribute); /* attribute */
+         SSVAL(param,2,max_matches); /* max count */
+         SSVAL(param,4,8+4+2); /* resume required + close on end + continue */
+         SSVAL(param,6,info_level); 
+         SIVAL(param,8,0);
+         strcpy(param+12,mask);
+       }
+      else
+       {
+         setup = TRANSACT2_FINDNEXT;
+         SSVAL(param,0,ff_dir_handle);
+         SSVAL(param,2,max_matches); /* max count */
+         SSVAL(param,4,info_level); 
+         SIVAL(param,6,ff_resume_key); /* ff_resume_key */
+         SSVAL(param,10,8+4+2);        /* resume required + close on end + continue */
+         strcpy(param+12,mask);
+
+         DEBUG(5,("hand=0x%X resume=%d ff_lastname=%d mask=%s\n",
+                  ff_dir_handle,ff_resume_key,ff_lastname,mask));
+       }
+      /* ??? original code added 1 pad byte after param */
+
+      send_trans_request(outbuf,SMBtrans2,NULL,FID_UNUSED,0,
+                        NULL,param,&setup,
+                        0,12+strlen(mask)+1,1,
+                        BUFFER_SIZE,10,0);
+
+      if (!receive_trans_response(inbuf,SMBtrans2,
+                             &resp_data_len,&resp_param_len,
+                                 &resp_data,&resp_param))
+       {
+         DEBUG(3,("FIND%s gave %s\n",First?"FIRST":"NEXT",smb_errstr(inbuf)));
+         break;
+       }
+
+      /* parse out some important return info */
+      p = resp_param;
+      if (First)
+       {
+         ff_dir_handle = SVAL(p,0);
+         ff_searchcount = SVAL(p,2);
+         ff_eos = SVAL(p,4);
+         ff_lastname = SVAL(p,8);
+       }
+      else
+       {
+         ff_searchcount = SVAL(p,0);
+         ff_eos = SVAL(p,2);
+         ff_lastname = SVAL(p,6);
+       }
+
+      if (ff_searchcount == 0) 
+       break;
+
+      /* point to the data bytes */
+      p = resp_data;
+
+      /* we might need the lastname for continuations */
+      if (ff_lastname > 0)
+       {
+         switch(info_level)
+           {
+           case 260:
+             ff_resume_key =0;
+             StrnCpy(mask,p+ff_lastname,resp_data_len-ff_lastname);
+             /* strcpy(mask,p+ff_lastname+94); */
+             break;
+           case 1:
+             strcpy(mask,p + ff_lastname + 1);
+             ff_resume_key = 0;
+             break;
+           }
+       }
+      else
+       strcpy(mask,"");
+  
+      /* and add them to the dirlist pool */
+      dirlist = Realloc(dirlist,dirlist_len + resp_data_len);
+
+      if (!dirlist)
+       {
+         DEBUG(0,("Failed to expand dirlist\n"));
+         break;
+       }
+
+      /* put in a length for the last entry, to ensure we can chain entries 
+        into the next packet */
+      {
+       char *p2;
+       for (p2=p,i=0;i<(ff_searchcount-1);i++)
+         p2 += interpret_long_filename(info_level,p2,NULL);
+       SSVAL(p2,0,resp_data_len - PTR_DIFF(p2,p));
+      }
+
+      /* grab the data for later use */
+      memcpy(dirlist+dirlist_len,p,resp_data_len);
+      dirlist_len += resp_data_len;
+
+      total_received += ff_searchcount;
+
+      if (resp_data) free(resp_data); resp_data = NULL;
+      if (resp_param) free(resp_param); resp_param = NULL;
+
+      DEBUG(3,("received %d entries (eos=%d resume=%d)\n",
+              ff_searchcount,ff_eos,ff_resume_key));
+
+      First = False;
+    }
+
+  if (!fn)
+    for (p=dirlist,i=0;i<total_received;i++)
+      {
+       p += interpret_long_filename(info_level,p,&finfo);
+       display_finfo(&finfo);
+      }
+
+  for (p=dirlist,i=0;i<total_received;i++)
+    {
+      p += interpret_long_filename(info_level,p,&finfo);
+      dir_action(inbuf,outbuf,attribute,&finfo,recurse_dir,fn,True);
+    }
+
+  /* free up the dirlist buffer */
+  if (dirlist) free(dirlist);
+  return(total_received);
+}
+
+
+/****************************************************************************
+  get a directory listing
+  ****************************************************************************/
+static void cmd_dir(char *inbuf,char *outbuf)
+{
+  int attribute = aDIR | aSYSTEM | aHIDDEN;
+  pstring mask;
+  fstring buf;
+  char *p=buf;
+
+  strcpy(mask,cur_dir);
+  if(mask[strlen(mask)-1]!='\\')
+    strcat(mask,"\\");
+
+  if (next_token(NULL,buf,NULL))
+    {
+      if (*p == '\\')
+       strcpy(mask,p);
+      else
+       strcat(mask,p);
+    }
+  else {
+    strcat(mask,"*");
+  }
+
+  do_dir(inbuf,outbuf,mask,attribute,NULL,recurse);
+
+  do_dskattr();
+}
+
+
+
+/****************************************************************************
+  get a file from rname to lname
+  ****************************************************************************/
+static void do_get(char *rname,char *lname,file_info *finfo1)
+{  
+  int handle=0,fnum;
+  uint32 nread=0;
+  char *p;
+  BOOL newhandle = False;
+  char *inbuf,*outbuf;
+  file_info finfo;
+  BOOL close_done = False;
+  BOOL ignore_close_error = False;
+  char *dataptr=NULL;
+  int datalen=0;
+
+  struct timeval tp_start;
+  GetTimeOfDay(&tp_start);
+
+  if (finfo1) 
+    finfo = *finfo1;
+  else
+    finfo = def_finfo;
+
+  if (lowercase)
+    strlower(lname);
+
+
+  inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+  outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+
+  if (!inbuf || !outbuf)
+    {
+      DEBUG(0,("out of memory\n"));
+      return;
+    }
+
+  bzero(outbuf,smb_size);
+  set_message(outbuf,15,1 + strlen(rname),True);
+
+  CVAL(outbuf,smb_com) = SMBopenX;
+  SSVAL(outbuf,smb_tid,cnum);
+  setup_pkt(outbuf);
+
+  SSVAL(outbuf,smb_vwv0,0xFF);
+  SSVAL(outbuf,smb_vwv2,1);
+  SSVAL(outbuf,smb_vwv3,(DENY_NONE<<4));
+  SSVAL(outbuf,smb_vwv4,aSYSTEM | aHIDDEN);
+  SSVAL(outbuf,smb_vwv5,aSYSTEM | aHIDDEN);
+  SSVAL(outbuf,smb_vwv8,1);
+  
+  p = smb_buf(outbuf);
+  strcpy(p,rname);
+  p = skip_string(p,1);
+
+  /* do a chained openX with a readX? */
+#if 1
+  if (finfo.size > 0)
+    {
+      DEBUG(3,("Chaining readX wth openX\n"));
+      SSVAL(outbuf,smb_vwv0,SMBreadX);
+      SSVAL(outbuf,smb_vwv1,smb_offset(p,outbuf));
+      bzero(p,200);
+      p -= smb_wct;
+      SSVAL(p,smb_wct,10);
+      SSVAL(p,smb_vwv0,0xFF);
+      SSVAL(p,smb_vwv5,MIN(max_xmit-500,finfo.size));
+      SSVAL(p,smb_vwv9,MIN(BUFFER_SIZE,finfo.size));
+      smb_setlen(outbuf,smb_len(outbuf)+11*2+1);  
+    }
+#endif
+
+  if(!strcmp(lname,"-"))
+    handle = fileno(stdout);
+  else 
+    {
+      handle = creat(lname,0644);
+      newhandle = True;
+    }
+  if (handle < 0)
+    {
+      DEBUG(0,("Error opening local file %s\n",lname));
+      free(inbuf);free(outbuf);
+      return;
+    }
+
+  send_smb(Client,outbuf);
+  receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+  if (CVAL(inbuf,smb_rcls) != 0)
+    {
+      if (CVAL(inbuf,smb_rcls) == ERRSRV &&
+         SVAL(inbuf,smb_err) == ERRnoresource &&
+         reopen_connection(inbuf,outbuf))
+       {
+         do_get(rname,lname,finfo1);
+         return;
+       }
+      DEBUG(0,("%s opening remote file %s\n",smb_errstr(inbuf),CNV_LANG(rname)));
+      if(newhandle)
+       close(handle);
+      free(inbuf);free(outbuf);
+      return;
+    }
+
+  strcpy(finfo.name,rname);
+
+  if (!finfo1)
+    {
+      finfo.mode = SVAL(inbuf,smb_vwv3);
+      /* these times arrive as LOCAL time, using the DST offset 
+        corresponding to that time, we convert them to GMT */
+      finfo.mtime = make_unix_date3(inbuf+smb_vwv4);
+      finfo.atime = finfo.ctime = finfo.mtime;
+      finfo.size = IVAL(inbuf,smb_vwv6);
+    }
+
+  DEBUG(3,("file %s attrib 0x%X\n",CNV_LANG(finfo.name),finfo.mode));
+
+  fnum = SVAL(inbuf,smb_vwv2);
+
+  /* we might have got some data from a chained readX */
+  if (SVAL(inbuf,smb_vwv0) == SMBreadX)
+    {
+      p = (smb_base(inbuf)+SVAL(inbuf,smb_vwv1)) - smb_wct;
+      datalen = SVAL(p,smb_vwv5);
+      dataptr = smb_base(inbuf) + SVAL(p,smb_vwv6);
+    }
+  else
+    {
+      dataptr = NULL;
+      datalen = 0;
+    }
+
+
+  DEBUG(2,("getting file %s of size %d bytes as %s ",
+          CNV_LANG(finfo.name),
+          finfo.size,
+          lname));
+
+  while (nread < finfo.size && !close_done)
+    {
+      int method = -1;
+      static BOOL can_chain_close = True;
+
+      p=NULL;
+      
+      DEBUG(3,("nread=%d max_xmit=%d fsize=%d\n",nread,max_xmit,finfo.size));
+
+      /* 3 possible read types. readbraw if a large block is required.
+        readX + close if not much left and read if neither is supported */
+
+      /* we might have already read some data from a chained readX */
+      if (dataptr && datalen>0)
+       method=3;
+
+      /* if we can finish now then readX+close */
+      if (method<0 && can_chain_close && (Protocol >= PROTOCOL_LANMAN1) && 
+         ((finfo.size - nread) < 
+          (max_xmit - (2*smb_size + 13*SIZEOFWORD + 300))))
+       method = 0;
+
+      /* if we support readraw then use that */
+      if (method<0 && readbraw_supported)
+       method = 1;
+
+      /* if we can then use readX */
+      if (method<0 && (Protocol >= PROTOCOL_LANMAN1))
+       method = 2;
+
+      switch (method)
+       {
+         /* use readX */
+       case 0:
+       case 2:
+         if (method == 0)
+           close_done = True;
+           
+         /* use readX + close */
+         bzero(outbuf,smb_size);
+         set_message(outbuf,10,0,True);
+         CVAL(outbuf,smb_com) = SMBreadX;
+         SSVAL(outbuf,smb_tid,cnum);
+         setup_pkt(outbuf);
+         
+         if (close_done)
+           {
+             CVAL(outbuf,smb_vwv0) = SMBclose;
+             SSVAL(outbuf,smb_vwv1,smb_offset(smb_buf(outbuf),outbuf));
+           }
+         else
+           CVAL(outbuf,smb_vwv0) = 0xFF;             
+         
+         SSVAL(outbuf,smb_vwv2,fnum);
+         SIVAL(outbuf,smb_vwv3,nread);
+         SSVAL(outbuf,smb_vwv5,MIN(max_xmit-200,finfo.size - nread));
+         SSVAL(outbuf,smb_vwv6,0);
+         SIVAL(outbuf,smb_vwv7,0);
+         SSVAL(outbuf,smb_vwv9,MIN(BUFFER_SIZE,finfo.size-nread));
+         
+         if (close_done)
+           {
+             p = smb_buf(outbuf);
+             bzero(p,9);
+             
+             CVAL(p,0) = 3;
+             SSVAL(p,1,fnum);
+             SIVALS(p,3,-1);
+             
+             /* now set the total packet length */
+             smb_setlen(outbuf,smb_len(outbuf)+9);
+           }
+         
+         send_smb(Client,outbuf);
+         receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+         
+         if (CVAL(inbuf,smb_rcls) != 0)
+           {
+             DEBUG(0,("Error %s reading remote file\n",smb_errstr(inbuf)));
+             break;
+           }
+         
+         if (close_done &&
+             SVAL(inbuf,smb_vwv0) != SMBclose)
+           {
+             /* NOTE: WfWg sometimes just ignores the chained
+                command! This seems to break the spec? */
+             DEBUG(3,("Rejected chained close?\n"));
+             close_done = False;
+             can_chain_close = False;
+             ignore_close_error = True;
+           }
+         
+         datalen = SVAL(inbuf,smb_vwv5);
+         dataptr = smb_base(inbuf) + SVAL(inbuf,smb_vwv6);
+         break;
+
+         /* use readbraw */
+       case 1:
+         {
+           static int readbraw_size = BUFFER_SIZE;
+         
+           extern int Client;
+           bzero(outbuf,smb_size);
+           set_message(outbuf,8,0,True);
+           CVAL(outbuf,smb_com) = SMBreadbraw;
+           SSVAL(outbuf,smb_tid,cnum);
+           setup_pkt(outbuf);
+           SSVAL(outbuf,smb_vwv0,fnum);
+           SIVAL(outbuf,smb_vwv1,nread);
+           SSVAL(outbuf,smb_vwv3,MIN(finfo.size-nread,readbraw_size));
+           SSVAL(outbuf,smb_vwv4,0);
+           SIVALS(outbuf,smb_vwv5,-1);
+           send_smb(Client,outbuf);
+
+           /* Now read the raw data into the buffer and write it */      
+           if(read_smb_length(Client,inbuf,0) == -1) {
+             DEBUG(0,("Failed to read length in readbraw\n"));     
+             exit(1);
+           }
+           
+           /* Even though this is not an smb message, smb_len
+              returns the generic length of an smb message */
+           datalen = smb_len(inbuf);
+
+           if (datalen == 0)
+             {
+               /* we got a readbraw error */
+               DEBUG(4,("readbraw error - reducing size\n"));
+               readbraw_size = (readbraw_size * 9) / 10;
+               
+               if (readbraw_size < max_xmit)
+                 {
+                   DEBUG(0,("disabling readbraw\n"));
+                   readbraw_supported = False;
+                 }
+               
+               dataptr=NULL;
+               continue;
+             }
+
+           if(read_data(Client,inbuf,datalen) != datalen) {
+             DEBUG(0,("Failed to read data in readbraw\n"));
+             exit(1);
+           }
+           dataptr = inbuf;
+         }
+         break;
+
+       case 3:
+         /* we've already read some data with a chained readX */
+         break;
+
+       default:
+         /* use plain read */
+         bzero(outbuf,smb_size);
+         set_message(outbuf,5,0,True);
+         CVAL(outbuf,smb_com) = SMBread;
+         SSVAL(outbuf,smb_tid,cnum);
+         setup_pkt(outbuf);
+
+         SSVAL(outbuf,smb_vwv0,fnum);
+         SSVAL(outbuf,smb_vwv1,MIN(max_xmit-200,finfo.size - nread));
+         SIVAL(outbuf,smb_vwv2,nread);
+         SSVAL(outbuf,smb_vwv4,finfo.size - nread);
+
+         send_smb(Client,outbuf);
+         receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+         if (CVAL(inbuf,smb_rcls) != 0)
+           {
+             DEBUG(0,("Error %s reading remote file\n",smb_errstr(inbuf)));
+             break;
+           }
+
+         datalen = SVAL(inbuf,smb_vwv0);
+         dataptr = smb_buf(inbuf) + 3;
+         break;
+       }
+      if (writefile(handle,dataptr,datalen) != datalen)
+       {
+         DEBUG(0,("Error writing local file\n"));
+         break;
+       }
+      
+      nread += datalen;
+      if (datalen == 0) 
+       {
+         DEBUG(0,("Error reading file %s. Got %d bytes\n",CNV_LANG(rname),nread));
+         break;
+       }
+
+      dataptr=NULL;
+      datalen=0;
+    }
+
+
+
+  if (!close_done)
+    {
+      bzero(outbuf,smb_size);
+      set_message(outbuf,3,0,True);
+      CVAL(outbuf,smb_com) = SMBclose;
+      SSVAL(outbuf,smb_tid,cnum);
+      setup_pkt(outbuf);
+      
+      SSVAL(outbuf,smb_vwv0,fnum);
+      SIVALS(outbuf,smb_vwv1,-1);
+      
+      send_smb(Client,outbuf);
+      receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+      
+      if (!ignore_close_error && CVAL(inbuf,smb_rcls) != 0)
+       {
+         DEBUG(0,("Error %s closing remote file\n",smb_errstr(inbuf)));
+         if(newhandle)
+           close(handle);
+         free(inbuf);free(outbuf);
+         return;
+       }
+    }
+
+  if(newhandle)
+    close(handle);
+
+  if (archive_level >= 2 && (finfo.mode & aARCH)) {
+    bzero(outbuf,smb_size);
+    set_message(outbuf,8,strlen(rname)+4,True);
+    CVAL(outbuf,smb_com) = SMBsetatr;
+    SSVAL(outbuf,smb_tid,cnum);
+    setup_pkt(outbuf);
+    SSVAL(outbuf,smb_vwv0,finfo.mode & ~(aARCH));
+    SIVALS(outbuf,smb_vwv1,0);
+    p = smb_buf(outbuf);
+    *p++ = 4;
+    strcpy(p,rname);
+    p += strlen(p)+1;
+    *p++ = 4;
+    *p = 0;
+    send_smb(Client,outbuf);
+    receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+  }
+
+  {
+    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 += finfo.size;
+
+    DEBUG(2,("(%g kb/s) (average %g kb/s)\n",
+            finfo.size / (1.024*this_time + 1.0e-4),
+            get_total_size / (1.024*get_total_time_ms)));
+  }
+
+  free(inbuf);free(outbuf);
+}
+
+
+/****************************************************************************
+  get a file
+  ****************************************************************************/
+static void cmd_get(void)
+{
+  pstring lname;
+  pstring rname;
+  char *p;
+
+  strcpy(rname,cur_dir);
+  strcat(rname,"\\");
+
+  p = rname + strlen(rname);
+
+  if (!next_token(NULL,p,NULL)) {
+    DEBUG(0,("get <filename>\n"));
+    return;
+  }
+  strcpy(lname,p);
+  dos_clean_name(rname);
+    
+  next_token(NULL,lname,NULL);
+
+  do_get(rname,lname,NULL);
+}
+
+
+/****************************************************************************
+  do a mget operation on one file
+  ****************************************************************************/
+static void do_mget(file_info *finfo)
+{
+  pstring rname;
+  pstring quest;
+
+  if (strequal(finfo->name,".") || strequal(finfo->name,".."))
+    return;
+
+  if (abort_mget)
+    {
+      DEBUG(0,("mget aborted\n"));
+      return;
+    }
+
+  if (finfo->mode & aDIR)
+    sprintf(quest,"Get directory %s? ",CNV_LANG(finfo->name));
+  else
+    sprintf(quest,"Get file %s? ",CNV_LANG(finfo->name));
+
+  if (prompt && !yesno(quest)) return;
+
+  if (finfo->mode & aDIR)
+    {
+      pstring saved_curdir;
+      pstring mget_mask;
+      char *inbuf,*outbuf;
+
+      inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+      outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+
+      if (!inbuf || !outbuf)
+       {
+         DEBUG(0,("out of memory\n"));
+         return;
+       }
+
+      strcpy(saved_curdir,cur_dir);
+
+      strcat(cur_dir,finfo->name);
+      strcat(cur_dir,"\\");
+
+      unix_format(finfo->name);
+      {
+       if (lowercase)
+         strlower(finfo->name);
+
+       if (!directory_exist(finfo->name,NULL) && 
+           sys_mkdir(finfo->name,0777) != 0) 
+         {
+           DEBUG(0,("failed to create directory %s\n",CNV_LANG(finfo->name)));
+           strcpy(cur_dir,saved_curdir);
+           free(inbuf);free(outbuf);
+           return;
+         }
+
+       if (sys_chdir(finfo->name) != 0)
+         {
+           DEBUG(0,("failed to chdir to directory %s\n",CNV_LANG(finfo->name)));
+           strcpy(cur_dir,saved_curdir);
+           free(inbuf);free(outbuf);
+           return;
+         }
+      }       
+
+      strcpy(mget_mask,cur_dir);
+      strcat(mget_mask,"*");
+      
+      do_dir((char *)inbuf,(char *)outbuf,
+            mget_mask,aSYSTEM | aHIDDEN | aDIR,do_mget,False);
+      chdir("..");
+      strcpy(cur_dir,saved_curdir);
+      free(inbuf);free(outbuf);
+    }
+  else
+    {
+      strcpy(rname,cur_dir);
+      strcat(rname,finfo->name);
+      do_get(rname,finfo->name,finfo);
+    }
+}
+
+/****************************************************************************
+view the file using the pager
+****************************************************************************/
+static void cmd_more(void)
+{
+  fstring rname,lname,tmpname,pager_cmd;
+  char *pager;
+
+  strcpy(rname,cur_dir);
+  strcat(rname,"\\");
+  sprintf(tmpname,"/tmp/smbmore.%d",getpid());
+  strcpy(lname,tmpname);
+
+  if (!next_token(NULL,rname+strlen(rname),NULL)) {
+    DEBUG(0,("more <filename>\n"));
+    return;
+  }
+  dos_clean_name(rname);
+
+  do_get(rname,lname,NULL);
+
+  pager=getenv("PAGER");
+  sprintf(pager_cmd,"%s %s",(pager? pager:PAGER), tmpname);
+  system(pager_cmd);
+  unlink(tmpname);
+}
+
+
+
+/****************************************************************************
+do a mget command
+****************************************************************************/
+static void cmd_mget(char *inbuf,char *outbuf)
+{
+  int attribute = aSYSTEM | aHIDDEN;
+  pstring mget_mask;
+  fstring buf;
+  char *p=buf;
+
+  *mget_mask = 0;
+
+  if (recurse)
+    attribute |= aDIR;
+
+  abort_mget = False;
+
+  while (next_token(NULL,p,NULL))
+    {
+      strcpy(mget_mask,cur_dir);
+      if(mget_mask[strlen(mget_mask)-1]!='\\')
+       strcat(mget_mask,"\\");
+
+      if (*p == '\\')
+       strcpy(mget_mask,p);
+      else
+       strcat(mget_mask,p);
+      do_dir((char *)inbuf,(char *)outbuf,mget_mask,attribute,do_mget,False);
+    }
+
+  if (! *mget_mask)
+    {
+      strcpy(mget_mask,cur_dir);
+      if(mget_mask[strlen(mget_mask)-1]!='\\')
+       strcat(mget_mask,"\\");
+      strcat(mget_mask,"*");
+      do_dir((char *)inbuf,(char *)outbuf,mget_mask,attribute,do_mget,False);
+    }
+}
+
+/****************************************************************************
+make a directory of name "name"
+****************************************************************************/
+static BOOL do_mkdir(char *name)
+{
+  char *p;
+  char *inbuf,*outbuf;
+
+  inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+  outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+
+  if (!inbuf || !outbuf)
+    {
+      DEBUG(0,("out of memory\n"));
+      return False;
+    }
+
+  bzero(outbuf,smb_size);
+  set_message(outbuf,0,2 + strlen(name),True);
+  
+  CVAL(outbuf,smb_com) = SMBmkdir;
+  SSVAL(outbuf,smb_tid,cnum);
+  setup_pkt(outbuf);
+
+  
+  p = smb_buf(outbuf);
+  *p++ = 4;      
+  strcpy(p,name);
+  
+  send_smb(Client,outbuf);
+  receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+  
+  if (CVAL(inbuf,smb_rcls) != 0)
+    {
+      DEBUG(0,("%s making remote directory %s\n",
+              smb_errstr(inbuf),CNV_LANG(name)));
+
+      free(inbuf);free(outbuf);
+      return(False);
+    }
+
+  free(inbuf);free(outbuf);
+  return(True);
+}
+
+
+/****************************************************************************
+  make a directory
+  ****************************************************************************/
+static void cmd_mkdir(char *inbuf,char *outbuf)
+{
+  pstring mask;
+  fstring buf;
+  char *p=buf;
+  
+  strcpy(mask,cur_dir);
+
+  if (!next_token(NULL,p,NULL))
+    {
+      if (!recurse)
+       DEBUG(0,("mkdir <dirname>\n"));
+      return;
+    }
+  strcat(mask,p);
+
+  if (recurse)
+    {
+      pstring ddir;
+      pstring ddir2;
+      *ddir2 = 0;
+
+      strcpy(ddir,mask);
+      trim_string(ddir,".",NULL);
+      p = strtok(ddir,"/\\");
+      while (p)
+       {
+         strcat(ddir2,p);
+         if (!chkpath(ddir2,False))
+           {             
+             do_mkdir(ddir2);
+           }
+         strcat(ddir2,"\\");
+         p = strtok(NULL,"/\\");
+       }        
+    }
+  else
+    do_mkdir(mask);
+}
+
+
+/*******************************************************************
+  write to a file using writebraw
+  ********************************************************************/
+static int smb_writeraw(char *outbuf,int fnum,int pos,char *buf,int n)
+{
+  extern int Client;
+  pstring inbuf;
+
+  bzero(outbuf,smb_size);
+  bzero(inbuf,smb_size);  
+  set_message(outbuf,Protocol>PROTOCOL_COREPLUS?12:10,0,True);
+
+  CVAL(outbuf,smb_com) = SMBwritebraw;
+  SSVAL(outbuf,smb_tid,cnum);
+  setup_pkt(outbuf);
+
+  SSVAL(outbuf,smb_vwv0,fnum);
+  SSVAL(outbuf,smb_vwv1,n);
+  SIVAL(outbuf,smb_vwv3,pos);
+  SSVAL(outbuf,smb_vwv7,1);
+
+  send_smb(Client,outbuf);
+  
+  if (!receive_smb(Client,inbuf,CLIENT_TIMEOUT) || CVAL(inbuf,smb_rcls) != 0)
+    return(0);
+
+  _smb_setlen(buf-4,n);                /* HACK! XXXX */
+
+  if (write_socket(Client,buf-4,n+4) != n+4)
+    return(0);
+
+  if (!receive_smb(Client,inbuf,CLIENT_TIMEOUT) || CVAL(inbuf,smb_rcls) != 0) {
+    DEBUG(0,("Error writing remote file (2)\n"));
+    return(0);
+  }
+  return(SVAL(inbuf,smb_vwv0));
+}
+      
+
+
+/*******************************************************************
+  write to a file
+  ********************************************************************/
+static int smb_writefile(char *outbuf,int fnum,int pos,char *buf,int n)
+{
+  pstring inbuf;
+
+  if (writebraw_supported && n > (max_xmit-200)) 
+    return(smb_writeraw(outbuf,fnum,pos,buf,n));
+
+  bzero(outbuf,smb_size);
+  bzero(inbuf,smb_size);
+  set_message(outbuf,5,n + 3,True);
+
+  CVAL(outbuf,smb_com) = SMBwrite;
+  SSVAL(outbuf,smb_tid,cnum);
+  setup_pkt(outbuf);
+
+  SSVAL(outbuf,smb_vwv0,fnum);
+  SSVAL(outbuf,smb_vwv1,n);
+  SIVAL(outbuf,smb_vwv2,pos);
+  SSVAL(outbuf,smb_vwv4,0);
+  CVAL(smb_buf(outbuf),0) = 1;
+  SSVAL(smb_buf(outbuf),1,n);
+
+  memcpy(smb_buf(outbuf)+3,buf,n);
+
+  send_smb(Client,outbuf);
+  receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+  if (CVAL(inbuf,smb_rcls) != 0) {
+    DEBUG(0,("%s writing remote file\n",smb_errstr(inbuf)));
+    return(0);
+  }
+  return(SVAL(inbuf,smb_vwv0));
+}
+      
+
+
+/****************************************************************************
+  put a single file
+  ****************************************************************************/
+static void do_put(char *rname,char *lname,file_info *finfo)
+{
+  int fnum;
+  FILE *f;
+  int nread=0;
+  char *p;
+  char *inbuf,*outbuf; 
+  time_t close_time = finfo->mtime;
+  char *buf=NULL;
+  static int maxwrite=0;
+
+  struct timeval tp_start;
+  GetTimeOfDay(&tp_start);
+
+  inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+  outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+
+  if (!inbuf || !outbuf)
+    {
+      DEBUG(0,("out of memory\n"));
+      return;
+    }
+
+  bzero(outbuf,smb_size);
+  set_message(outbuf,3,2 + strlen(rname),True);
+
+  if (finfo->mtime == 0 || finfo->mtime == -1)
+    finfo->mtime = finfo->atime = finfo->ctime = time(NULL);
+
+  CVAL(outbuf,smb_com) = SMBcreate;
+  SSVAL(outbuf,smb_tid,cnum);
+  setup_pkt(outbuf);
+
+  SSVAL(outbuf,smb_vwv0,finfo->mode);
+  put_dos_date3(outbuf,smb_vwv1,finfo->mtime);
+  
+  p = smb_buf(outbuf);
+  *p++ = 4;      
+  strcpy(p,rname);
+  
+  send_smb(Client,outbuf);
+  receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+  
+  if (CVAL(inbuf,smb_rcls) != 0)
+    {
+      DEBUG(0,("%s opening remote file %s\n",smb_errstr(inbuf),CNV_LANG(rname)));
+
+      free(inbuf);free(outbuf);if (buf) free(buf);
+      return;
+    }
+
+  f = fopen(lname,"r");
+
+  if (!f)
+    {
+      DEBUG(0,("Error opening local file %s\n",lname));
+      free(inbuf);free(outbuf);
+      return;
+    }
+
+  
+  fnum = SVAL(inbuf,smb_vwv0);
+  if (finfo->size < 0)
+    finfo->size = file_size(lname);
+  
+  DEBUG(1,("putting file %s of size %d bytes as %s ",lname,finfo->size,CNV_LANG(rname)));
+  
+  if (!maxwrite)
+    maxwrite = writebraw_supported?MAX(max_xmit,BUFFER_SIZE):(max_xmit-200);
+
+  while (nread < finfo->size)
+    {
+      int n = maxwrite;
+      int ret;
+
+      n = MIN(n,finfo->size - nread);
+
+      buf = (char *)Realloc(buf,n+4);
+  
+      fseek(f,nread,SEEK_SET);
+      if ((n = readfile(buf+4,1,n,f)) < 1)
+       {
+         DEBUG(0,("Error reading local file\n"));
+         break;
+       }         
+
+      ret = smb_writefile(outbuf,fnum,nread,buf+4,n);
+
+      if (n != ret) {
+       if (!maxwrite) {
+         DEBUG(0,("Error writing file\n"));
+         break;
+       } else {
+         maxwrite /= 2;
+         continue;
+       }
+      }
+
+      nread += n;
+    }
+
+
+
+  bzero(outbuf,smb_size);
+  set_message(outbuf,3,0,True);
+  CVAL(outbuf,smb_com) = SMBclose;
+  SSVAL(outbuf,smb_tid,cnum);
+  setup_pkt(outbuf);
+
+  SSVAL(outbuf,smb_vwv0,fnum);  
+  put_dos_date3(outbuf,smb_vwv1,close_time);
+
+  send_smb(Client,outbuf);
+  receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+  
+  if (CVAL(inbuf,smb_rcls) != 0)
+    {
+      DEBUG(0,("%s closing remote file %s\n",smb_errstr(inbuf),CNV_LANG(rname)));
+      fclose(f);
+      free(inbuf);free(outbuf);
+      if (buf) free(buf);
+      return;
+    }
+
+  
+  fclose(f);
+  free(inbuf);free(outbuf);
+  if (buf) 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 += finfo->size;
+
+    DEBUG(2,("(%g kb/s) (average %g kb/s)\n",
+            finfo->size / (1.024*this_time + 1.0e-4),
+            put_total_size / (1.024*put_total_time_ms)));
+  }
+} 
+
+
+/****************************************************************************
+  put a file
+  ****************************************************************************/
+static void cmd_put(void)
+{
+  pstring lname;
+  pstring rname;
+  fstring buf;
+  char *p=buf;
+  file_info finfo;
+  finfo = def_finfo;
+  
+  strcpy(rname,cur_dir);
+  strcat(rname,"\\");
+  
+  
+  if (!next_token(NULL,p,NULL))
+    {
+      DEBUG(0,("put <filename>\n"));
+      return;
+    }
+  strcpy(lname,p);
+  
+  if (next_token(NULL,p,NULL))
+    strcat(rname,p);      
+  else
+    strcat(rname,lname);
+
+  dos_clean_name(rname);
+
+  {
+    struct stat st;
+    if (!file_exist(lname,&st)) {
+      DEBUG(0,("%s does not exist\n",lname));
+      return;
+    }
+    finfo.mtime = st.st_mtime;
+  }
+
+  do_put(rname,lname,&finfo);
+}
+
+/****************************************************************************
+  seek in a directory/file list until you get something that doesn't start with
+  the specified name
+  ****************************************************************************/
+static BOOL seek_list(FILE *f,char *name)
+{
+  pstring s;
+  while (!feof(f))
+    {
+      if (fscanf(f,"%s",s) != 1) return(False);
+      trim_string(s,"./",NULL);
+      if (strncmp(s,name,strlen(name)) != 0)
+       {
+         strcpy(name,s);
+         return(True);
+       }
+    }
+      
+  return(False);
+}
+
+
+/****************************************************************************
+  set the file selection mask
+  ****************************************************************************/
+static void cmd_select(void)
+{
+  strcpy(fileselection,"");
+  next_token(NULL,fileselection,NULL);
+}
+
+
+/****************************************************************************
+  mput some files
+  ****************************************************************************/
+static void cmd_mput(void)
+{
+  pstring lname;
+  pstring rname;
+  file_info finfo;
+  fstring buf;
+  char *p=buf;
+
+  finfo = def_finfo;
+
+  
+  while (next_token(NULL,p,NULL))
+    {
+      struct stat st;
+      pstring cmd;
+      pstring tmpname;
+      FILE *f;
+      
+      sprintf(tmpname,"/tmp/ls.smb.%d",(int)getpid());
+      if (recurse)
+       sprintf(cmd,"find . -name \"%s\" -print > %s",p,tmpname);
+      else
+       sprintf(cmd,"/bin/ls %s > %s",p,tmpname);
+      system(cmd);
+
+      f = fopen(tmpname,"r");
+      if (!f) continue;
+
+      while (!feof(f))
+       {
+         pstring quest;
+
+         if (fscanf(f,"%s",lname) != 1) break;
+         trim_string(lname,"./",NULL);
+
+       again1:
+
+         /* check if it's a directory */
+         if (directory_exist(lname,&st))
+           {
+             if (!recurse) continue;
+             sprintf(quest,"Put directory %s? ",lname);
+             if (prompt && !yesno(quest)) 
+               {
+                 strcat(lname,"/");
+                 if (!seek_list(f,lname))
+                   break;
+                 goto again1;              
+               }
+             
+             strcpy(rname,cur_dir);
+             strcat(rname,lname);
+             if (!do_mkdir(rname))
+               {
+                 strcat(lname,"/");
+                 if (!seek_list(f,lname))
+                   break;
+                 goto again1;                            
+               }
+
+             continue;
+           }
+         else
+           {
+             sprintf(quest,"Put file %s? ",lname);
+             if (prompt && !yesno(quest)) continue;
+
+             strcpy(rname,cur_dir);
+             strcat(rname,lname);
+           }
+         dos_format(rname);
+
+         /* null size so do_put knows to ignore it */
+         finfo.size = -1;
+
+         /* set the date on the file */
+         finfo.mtime = st.st_mtime;
+
+         do_put(rname,lname,&finfo);
+       }
+      fclose(f);
+      unlink(tmpname);
+    }
+}
+
+/****************************************************************************
+  cancel a print job
+  ****************************************************************************/
+static void do_cancel(int job)
+{
+  char *rparam = NULL;
+  char *rdata = NULL;
+  char *p;
+  int rdrcnt,rprcnt;
+  pstring param;
+
+  bzero(param,sizeof(param));
+
+  p = param;
+  SSVAL(p,0,81);               /* api number */
+  p += 2;
+  strcpy(p,"W");
+  p = skip_string(p,1);
+  strcpy(p,"");
+  p = skip_string(p,1);
+  SSVAL(p,0,job);     
+  p += 2;
+
+  if (call_api(PTR_DIFF(p,param),0,
+              6,1000,
+              &rprcnt,&rdrcnt,
+              param,NULL,
+              &rparam,&rdata))
+    {
+      int res = SVAL(rparam,0);
+
+      if (!res)
+       printf("Job %d cancelled\n",job);
+      else
+       printf("Error %d calcelling job %d\n",res,job);
+      return;
+    }
+  else
+  printf("Server refused cancel request\n");
+
+  if (rparam) free(rparam);
+  if (rdata) free(rdata);
+
+  return;
+}
+
+
+/****************************************************************************
+  cancel a print job
+  ****************************************************************************/
+static void cmd_cancel(char *inbuf,char *outbuf )
+{
+  fstring buf;
+  int job; 
+
+  if (!connect_as_printer)
+    {
+      DEBUG(0,("WARNING: You didn't use the -P option to smbclient.\n"));
+      DEBUG(0,("Trying to cancel print jobs without -P may fail\n"));
+    }
+
+  if (!next_token(NULL,buf,NULL)) {
+    printf("cancel <jobid> ...\n");
+    return;
+  }
+  do {
+    job = atoi(buf);
+    do_cancel(job);
+  } while (next_token(NULL,buf,NULL));
+}
+
+
+/****************************************************************************
+  get info on a file
+  ****************************************************************************/
+static void cmd_stat(char *inbuf,char *outbuf)
+{
+  fstring buf;
+  pstring param;
+  char *resp_data=NULL;
+  char *resp_param=NULL;
+  int resp_data_len = 0;
+  int resp_param_len=0;
+  char *p;
+  uint16 setup = TRANSACT2_QPATHINFO;
+
+  if (!next_token(NULL,buf,NULL)) {
+    printf("stat <file>\n");
+    return;
+  }
+
+  bzero(param,6);
+  SSVAL(param,0,4); /* level */
+  p = param+6;
+  strcpy(p,cur_dir);
+  strcat(p,buf);
+
+  send_trans_request(outbuf,SMBtrans2,NULL,FID_UNUSED,0,
+                    NULL,param,&setup,
+                    0,6 + strlen(p)+1,1,
+                    BUFFER_SIZE,2,0);
+
+  receive_trans_response(inbuf,SMBtrans2,
+                         &resp_data_len,&resp_param_len,
+                         &resp_data,&resp_param);
+
+  if (resp_data) free(resp_data); resp_data = NULL;
+  if (resp_param) free(resp_param); resp_param = NULL;
+}
+
+
+/****************************************************************************
+  print a file
+  ****************************************************************************/
+static void cmd_print(char *inbuf,char *outbuf )
+{
+  int fnum;
+  FILE *f = NULL;
+  uint32 nread=0;
+  pstring lname;
+  pstring rname;
+  char *p;
+
+  if (!connect_as_printer)
+    {
+      DEBUG(0,("WARNING: You didn't use the -P option to smbclient.\n"));
+      DEBUG(0,("Trying to print without -P may fail\n"));
+    }
+
+  if (!next_token(NULL,lname,NULL))
+    {
+      DEBUG(0,("print <filename>\n"));
+      return;
+    }
+
+  strcpy(rname,lname);
+  p = strrchr(rname,'/');
+  if (p)
+    {
+      pstring tname;
+      strcpy(tname,p+1);
+      strcpy(rname,tname);
+    }
+
+  if ((int)strlen(rname) > 14)
+    rname[14] = 0;
+
+  if (strequal(lname,"-"))
+    {
+      f = stdin;
+      strcpy(rname,"stdin");
+    }
+  
+  dos_clean_name(rname);
+
+  bzero(outbuf,smb_size);
+  set_message(outbuf,2,2 + strlen(rname),True);
+  
+  CVAL(outbuf,smb_com) = SMBsplopen;
+  SSVAL(outbuf,smb_tid,cnum);
+  setup_pkt(outbuf);
+
+  SSVAL(outbuf,smb_vwv0,0);
+  SSVAL(outbuf,smb_vwv1,printmode);
+  
+  p = smb_buf(outbuf);
+  *p++ = 4;      
+  strcpy(p,rname);
+  
+  send_smb(Client,outbuf);
+  receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+  
+  if (CVAL(inbuf,smb_rcls) != 0)
+    {
+      DEBUG(0,("%s opening printer for %s\n",smb_errstr(inbuf),CNV_LANG(rname)));
+      return;
+    }
+  
+  if (!f)
+    f = fopen(lname,"r");
+  if (!f)
+    {
+      DEBUG(0,("Error opening local file %s\n",lname));
+      return;
+    }
+
+  
+  fnum = SVAL(inbuf,smb_vwv0);
+  
+  DEBUG(1,("printing file %s as %s\n",lname,CNV_LANG(rname)));
+  
+  while (!feof(f))
+    {
+      int n;
+  
+      bzero(outbuf,smb_size);
+      set_message(outbuf,1,3,True);
+
+      /* for some strange reason the OS/2 print server can't handle large
+        packets when printing. weird */
+      n = MIN(1024,max_xmit-(smb_len(outbuf)+4));
+
+      if (translation)
+       n = printread(f,smb_buf(outbuf)+3,(int)(0.95*n));
+      else
+       n = readfile(smb_buf(outbuf)+3,1,n,f);
+      if (n <= 0) 
+       {
+         DEBUG(0,("read gave %d\n",n));
+         break;
+       }
+
+      smb_setlen(outbuf,smb_len(outbuf) + n);
+
+      CVAL(outbuf,smb_com) = SMBsplwr;
+      SSVAL(outbuf,smb_tid,cnum);
+      setup_pkt(outbuf);
+
+      SSVAL(outbuf,smb_vwv0,fnum);
+      SSVAL(outbuf,smb_vwv1,n+3);
+      CVAL(smb_buf(outbuf),0) = 1;
+      SSVAL(smb_buf(outbuf),1,n);
+
+      send_smb(Client,outbuf);
+      receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+      if (CVAL(inbuf,smb_rcls) != 0)
+       {
+         DEBUG(0,("%s printing remote file\n",smb_errstr(inbuf)));
+         break;
+       }
+
+      nread += n;
+    }
+
+  DEBUG(2,("%d bytes printed\n",nread));
+
+  bzero(outbuf,smb_size);
+  set_message(outbuf,1,0,True);
+  CVAL(outbuf,smb_com) = SMBsplclose;
+  SSVAL(outbuf,smb_tid,cnum);
+  setup_pkt(outbuf);
+
+  SSVAL(outbuf,smb_vwv0,fnum);
+
+  send_smb(Client,outbuf);
+  receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+  
+  if (CVAL(inbuf,smb_rcls) != 0)
+    {
+      DEBUG(0,("%s closing print file\n",smb_errstr(inbuf)));
+      if (f != stdin)
+       fclose(f);
+      return;
+    }
+
+  if (f != stdin)
+    fclose(f);
+}
+
+/****************************************************************************
+print a file
+****************************************************************************/
+static void cmd_queue(char *inbuf,char *outbuf )
+{
+  int count;
+  char *p;
+
+  bzero(outbuf,smb_size);
+  set_message(outbuf,2,0,True);
+  
+  CVAL(outbuf,smb_com) = SMBsplretq;
+  SSVAL(outbuf,smb_tid,cnum);
+  setup_pkt(outbuf);
+
+  SSVAL(outbuf,smb_vwv0,32); /* a max of 20 entries is to be shown */
+  SSVAL(outbuf,smb_vwv1,0); /* the index into the queue */
+  
+  send_smb(Client,outbuf);
+  receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+  
+  if (CVAL(inbuf,smb_rcls) != 0)
+    {
+      DEBUG(0,("%s obtaining print queue\n",smb_errstr(inbuf)));
+      return;
+    }
+
+  count = SVAL(inbuf,smb_vwv0);
+  p = smb_buf(inbuf) + 3;
+  if (count <= 0)
+    {
+      DEBUG(0,("No entries in the print queue\n"));
+      return;
+    }  
+
+  {
+    char status[20];
+
+    DEBUG(0,("Job      Name              Size         Status\n"));
+
+    while (count--)
+      {
+       switch (CVAL(p,4))
+         {
+         case 0x01: sprintf(status,"held or stopped"); break;
+         case 0x02: sprintf(status,"printing"); break;
+         case 0x03: sprintf(status,"awaiting print"); break;
+         case 0x04: sprintf(status,"in intercept"); break;
+         case 0x05: sprintf(status,"file had error"); break;
+         case 0x06: sprintf(status,"printer error"); break;
+         default: sprintf(status,"unknown"); break;
+         }
+
+       DEBUG(0,("%-6d   %-16.16s  %-9d    %s\n",
+                SVAL(p,5),p+12,IVAL(p,7),status));
+       p += 28;
+      }
+  }
+  
+}
+
+
+/****************************************************************************
+delete some files
+****************************************************************************/
+static void do_del(file_info *finfo)
+{
+  char *p;
+  char *inbuf,*outbuf;
+  pstring mask;
+
+  strcpy(mask,cur_dir);
+  strcat(mask,finfo->name);
+
+  if (finfo->mode & aDIR) 
+    return;
+
+  inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+  outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+  
+  if (!inbuf || !outbuf)
+    {
+      DEBUG(0,("out of memory\n"));
+      return;
+    }
+
+  bzero(outbuf,smb_size);
+  set_message(outbuf,1,2 + strlen(mask),True);
+  
+  CVAL(outbuf,smb_com) = SMBunlink;
+  SSVAL(outbuf,smb_tid,cnum);
+  setup_pkt(outbuf);
+
+  SSVAL(outbuf,smb_vwv0,0);
+  
+  p = smb_buf(outbuf);
+  *p++ = 4;      
+  strcpy(p,mask);
+  
+  send_smb(Client,outbuf);
+  receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+  
+  if (CVAL(inbuf,smb_rcls) != 0)
+    DEBUG(0,("%s deleting remote file %s\n",smb_errstr(inbuf),CNV_LANG(mask)));
+
+  free(inbuf);free(outbuf);
+  
+}
+
+/****************************************************************************
+delete some files
+****************************************************************************/
+static void cmd_del(char *inbuf,char *outbuf )
+{
+  pstring mask;
+  fstring buf;
+  int attribute = aSYSTEM | aHIDDEN;
+
+  if (recurse)
+    attribute |= aDIR;
+  
+  strcpy(mask,cur_dir);
+    
+  if (!next_token(NULL,buf,NULL))
+    {
+      DEBUG(0,("del <filename>\n"));
+      return;
+    }
+  strcat(mask,buf);
+
+  do_dir((char *)inbuf,(char *)outbuf,mask,attribute,do_del,False);
+}
+
+
+/****************************************************************************
+remove a directory
+****************************************************************************/
+static void cmd_rmdir(char *inbuf,char *outbuf )
+{
+  pstring mask;
+  fstring buf;
+  char *p;
+  
+  strcpy(mask,cur_dir);
+  
+  if (!next_token(NULL,buf,NULL))
+    {
+      DEBUG(0,("rmdir <dirname>\n"));
+      return;
+    }
+  strcat(mask,buf);
+
+  bzero(outbuf,smb_size);
+  set_message(outbuf,0,2 + strlen(mask),True);
+  
+  CVAL(outbuf,smb_com) = SMBrmdir;
+  SSVAL(outbuf,smb_tid,cnum);
+  setup_pkt(outbuf);
+
+  
+  p = smb_buf(outbuf);
+  *p++ = 4;      
+  strcpy(p,mask);
+  
+  send_smb(Client,outbuf);
+  receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+  
+  if (CVAL(inbuf,smb_rcls) != 0)
+    {
+      DEBUG(0,("%s removing remote directory file %s\n",smb_errstr(inbuf),CNV_LANG(mask)));
+      return;
+    }
+  
+}
+
+/****************************************************************************
+rename some files
+****************************************************************************/
+static void cmd_rename(char *inbuf,char *outbuf )
+{
+  pstring src,dest;
+  fstring buf,buf2;
+  char *p;
+  
+  strcpy(src,cur_dir);
+  strcpy(dest,cur_dir);
+  
+  if (!next_token(NULL,buf,NULL) || !next_token(NULL,buf2,NULL))
+    {
+      DEBUG(0,("rename <src> <dest>\n"));
+      return;
+    }
+  strcat(src,buf);
+  strcat(dest,buf2);
+
+  bzero(outbuf,smb_size);
+  set_message(outbuf,1,4 + strlen(src) + strlen(dest),True);
+  
+  CVAL(outbuf,smb_com) = SMBmv;
+  SSVAL(outbuf,smb_tid,cnum);
+  SSVAL(outbuf,smb_vwv0,aHIDDEN | aDIR | aSYSTEM);
+  setup_pkt(outbuf);
+  
+  p = smb_buf(outbuf);
+  *p++ = 4;      
+  strcpy(p,src);
+  p = skip_string(p,1);
+  *p++ = 4;      
+  strcpy(p,dest);
+  
+  send_smb(Client,outbuf);
+  receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+  
+  if (CVAL(inbuf,smb_rcls) != 0)
+    {
+      DEBUG(0,("%s renaming files\n",smb_errstr(inbuf)));
+      return;
+    }
+  
+}
+
+
+/****************************************************************************
+toggle the prompt flag
+****************************************************************************/
+static void cmd_prompt(void)
+{
+  prompt = !prompt;
+  DEBUG(2,("prompting is now %s\n",prompt?"on":"off"));
+}
+
+
+/****************************************************************************
+set the newer than time
+****************************************************************************/
+static void cmd_newer(void)
+{
+  fstring buf;
+  BOOL ok;
+  struct stat sbuf;
+
+  ok = next_token(NULL,buf,NULL);
+  if (ok && (sys_stat(buf,&sbuf) == 0))
+    {
+      newer_than = sbuf.st_mtime;
+      DEBUG(1,("Getting files newer than %s",
+              asctime(LocalTime(&newer_than,GMT_TO_LOCAL))));
+    }
+  else
+    newer_than = 0;
+
+  if (ok && newer_than == 0)
+    DEBUG(0,("Error setting newer-than time\n"));
+}
+
+/****************************************************************************
+set the archive level
+****************************************************************************/
+static void cmd_archive(void)
+{
+  fstring buf;
+
+  if (next_token(NULL,buf,NULL)) {
+    archive_level = atoi(buf);
+  } else
+    DEBUG(0,("Archive level is %d\n",archive_level));
+}
+
+/****************************************************************************
+toggle the lowercaseflag
+****************************************************************************/
+static void cmd_lowercase(void)
+{
+  lowercase = !lowercase;
+  DEBUG(2,("filename lowercasing is now %s\n",lowercase?"on":"off"));
+}
+
+
+
+
+/****************************************************************************
+toggle the recurse flag
+****************************************************************************/
+static void cmd_recurse(void)
+{
+  recurse = !recurse;
+  DEBUG(2,("directory recursion is now %s\n",recurse?"on":"off"));
+}
+
+/****************************************************************************
+toggle the translate flag
+****************************************************************************/
+static void cmd_translate(void)
+{
+  translation = !translation;
+  DEBUG(2,("CR/LF<->LF and print text translation now %s\n",
+       translation?"on":"off"));
+}
+
+
+/****************************************************************************
+do a printmode command
+****************************************************************************/
+static void cmd_printmode(void)
+{
+  fstring buf;
+  fstring mode;
+
+  if (next_token(NULL,buf,NULL))
+    {
+      if (strequal(buf,"text"))
+       printmode = 0;      
+      else
+       {
+         if (strequal(buf,"graphics"))
+           printmode = 1;
+         else
+           printmode = atoi(buf);
+       }
+    }
+
+  switch(printmode)
+    {
+    case 0: 
+      strcpy(mode,"text");
+      break;
+    case 1: 
+      strcpy(mode,"graphics");
+      break;
+    default: 
+      sprintf(mode,"%d",printmode);
+      break;
+    }
+
+  DEBUG(2,("the printmode is now %s\n",mode));
+}
+
+/****************************************************************************
+do the lcd command
+****************************************************************************/
+static void cmd_lcd(void)
+{
+  fstring buf;
+  pstring d;
+
+  if (next_token(NULL,buf,NULL))
+    sys_chdir(buf);
+  DEBUG(2,("the local directory is now %s\n",GetWd(d)));
+}
+
+
+/****************************************************************************
+send a session request
+****************************************************************************/
+static BOOL send_session_request(char *inbuf,char *outbuf)
+{
+  fstring dest;
+  char *p;
+  int len = 4;
+  /* send a session request (RFC 8002) */
+
+  strcpy(dest,desthost);
+  p = strchr(dest,'.');
+  if (p) *p = 0;
+
+  /* put in the destination name */
+  p = outbuf+len;
+  name_mangle(dest,p,name_type);
+  len += name_len(p);
+
+  /* and my name */
+  p = outbuf+len;
+  name_mangle(myname,p,0);
+  len += name_len(p);
+
+  /* setup the packet length */
+  _smb_setlen(outbuf,len);
+  CVAL(outbuf,0) = 0x81;
+
+  send_smb(Client,outbuf);
+  DEBUG(5,("Sent session request\n"));
+
+  receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+  if (CVAL(inbuf,0) == 0x84) /* C. Hoch  9/14/95 Start */
+    {
+      /* For information, here is the response structure.
+       * We do the byte-twiddling to for portability.
+       struct RetargetResponse{
+       unsigned char type;
+       unsigned char flags;
+       int16 length;
+       int32 ip_addr;
+       int16 port;
+       };
+       */
+      extern int Client;
+      int port = (CVAL(inbuf,8)<<8)+CVAL(inbuf,9);
+      /* SESSION RETARGET */
+      putip((char *)&dest_ip,inbuf+4);
+
+      close_sockets();
+      Client = open_socket_out(SOCK_STREAM, &dest_ip, port);
+      if (Client == -1)
+        return False;
+
+      DEBUG(3,("Retargeted\n"));
+
+      set_socket_options(Client,user_socket_options);
+
+      /* Try again */
+      return send_session_request(inbuf,outbuf);
+    } /* C. Hoch 9/14/95 End */
+
+
+  if (CVAL(inbuf,0) != 0x82)
+    {
+      int ecode = CVAL(inbuf,4);
+      DEBUG(0,("Session request failed (%d,%d) with myname=%s destname=%s\n",
+              CVAL(inbuf,0),ecode,myname,desthost));
+      switch (ecode)
+       {
+       case 0x80: 
+         DEBUG(0,("Not listening on called name\n")); 
+         DEBUG(0,("Try to connect to another name (instead of %s)\n",desthost));
+         DEBUG(0,("You may find the -I option useful for this\n"));
+         break;
+       case 0x81: 
+         DEBUG(0,("Not listening for calling name\n")); 
+         DEBUG(0,("Try to connect as another name (instead of %s)\n",myname));
+         DEBUG(0,("You may find the -n option useful for this\n"));
+         break;
+       case 0x82: 
+         DEBUG(0,("Called name not present\n")); 
+         DEBUG(0,("Try to connect to another name (instead of %s)\n",desthost));
+         DEBUG(0,("You may find the -I option useful for this\n"));
+         break;
+       case 0x83: 
+         DEBUG(0,("Called name present, but insufficient resources\n")); 
+         DEBUG(0,("Perhaps you should try again later?\n")); 
+         break;
+       default:
+         DEBUG(0,("Unspecified error 0x%X\n",ecode)); 
+         DEBUG(0,("Your server software is being unfriendly\n"));
+         break;          
+       }
+      return(False);
+    }
+  return(True);
+}
+
+
+/****************************************************************************
+send a login command
+****************************************************************************/
+static BOOL send_login(char *inbuf,char *outbuf,BOOL start_session,BOOL use_setup)
+{
+  BOOL was_null = (!inbuf && !outbuf);
+  int sesskey=0;
+  time_t servertime = 0;
+  extern int serverzone;
+  int sec_mode=0;
+  int crypt_len;
+  int max_vcs=0;
+  struct {
+    int prot;
+    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_LANMAN2,"LM1.2X002"},
+      {PROTOCOL_LANMAN2,"Samba"},
+      {PROTOCOL_NT1,"NT LM 0.12"},
+      {PROTOCOL_NT1,"NT LANMAN 1.0"},
+      {-1,NULL}
+    };
+  char *pass = NULL;  
+  pstring dev;
+  char *p;
+  int numprots;
+
+  if (was_null)
+    {
+      inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+      outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+    }
+
+#if AJT
+  if (strstr(service,"IPC$")) connect_as_ipc = True;
+#endif
+
+  strcpy(dev,"A:");
+  if (connect_as_printer)
+    strcpy(dev,"LPT1:");
+  if (connect_as_ipc)
+    strcpy(dev,"IPC");
+
+
+  if (start_session && !send_session_request(inbuf,outbuf))
+    {
+      if (was_null)
+       {
+         free(inbuf);
+         free(outbuf);
+       }      
+      return(False);
+    }
+
+  bzero(outbuf,smb_size);
+
+  /* setup the protocol strings */
+  {
+    int plength;
+
+    for (plength=0,numprots=0;
+        prots[numprots].name && prots[numprots].prot<=max_protocol;
+        numprots++)
+      plength += strlen(prots[numprots].name)+2;
+    
+    set_message(outbuf,0,plength,True);
+
+    p = smb_buf(outbuf);
+    for (numprots=0;
+        prots[numprots].name && prots[numprots].prot<=max_protocol;
+        numprots++)
+      {
+       *p++ = 2;
+       strcpy(p,prots[numprots].name);
+       p += strlen(p) + 1;
+      }
+  }
+
+  CVAL(outbuf,smb_com) = SMBnegprot;
+  setup_pkt(outbuf);
+
+  CVAL(smb_buf(outbuf),0) = 2;
+
+  send_smb(Client,outbuf);
+  receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+  show_msg(inbuf);
+
+  if (CVAL(inbuf,smb_rcls) != 0 || ((int)SVAL(inbuf,smb_vwv0) >= numprots))
+    {
+      DEBUG(0,("SMBnegprot failed. myname=%s destname=%s - %s \n",
+           myname,desthost,smb_errstr(inbuf)));
+      if (was_null)
+       {
+         free(inbuf);
+         free(outbuf);
+       }
+      return(False);
+    }
+
+  Protocol = prots[SVAL(inbuf,smb_vwv0)].prot;
+
+
+  if (Protocol < PROTOCOL_NT1) {    
+    sec_mode = SVAL(inbuf,smb_vwv1);
+    max_xmit = SVAL(inbuf,smb_vwv2);
+    sesskey = IVAL(inbuf,smb_vwv6);
+    serverzone = SVALS(inbuf,smb_vwv10)*60;
+    /* this time is converted to GMT by make_unix_date */
+    servertime = make_unix_date(inbuf+smb_vwv8);
+    if (Protocol >= PROTOCOL_COREPLUS) {
+      readbraw_supported = ((SVAL(inbuf,smb_vwv5) & 0x1) != 0);
+      writebraw_supported = ((SVAL(inbuf,smb_vwv5) & 0x2) != 0);
+    }
+    crypt_len = smb_buflen(inbuf);
+    memcpy(cryptkey,smb_buf(inbuf),8);
+    DEBUG(3,("max mux %d\n",SVAL(inbuf,smb_vwv3)));
+    max_vcs = SVAL(inbuf,smb_vwv4); 
+    DEBUG(3,("max vcs %d\n",max_vcs)); 
+    DEBUG(3,("max blk %d\n",SVAL(inbuf,smb_vwv5)));
+  } else {
+    /* NT protocol */
+    sec_mode = CVAL(inbuf,smb_vwv1);
+    max_xmit = IVAL(inbuf,smb_vwv3+1);
+    sesskey = IVAL(inbuf,smb_vwv7+1);
+    serverzone = SVALS(inbuf,smb_vwv15+1)*60;
+    /* this time arrives in real GMT */
+    servertime = interpret_long_date(inbuf+smb_vwv11+1);
+    crypt_len = CVAL(inbuf,smb_vwv16+1);
+    memcpy(cryptkey,smb_buf(inbuf),8);
+    if (IVAL(inbuf,smb_vwv9+1) & 1)
+      readbraw_supported = writebraw_supported = True;      
+    DEBUG(3,("max mux %d\n",SVAL(inbuf,smb_vwv1+1)));
+    max_vcs = SVAL(inbuf,smb_vwv2+1); 
+    DEBUG(3,("max vcs %d\n",max_vcs));
+    DEBUG(3,("max raw %d\n",IVAL(inbuf,smb_vwv5+1)));
+    DEBUG(3,("capabilities 0x%x\n",IVAL(inbuf,smb_vwv9+1)));
+  }
+
+  DEBUG(3,("Sec mode %d\n",SVAL(inbuf,smb_vwv1)));
+  DEBUG(3,("max xmt %d\n",max_xmit));
+  DEBUG(3,("Got %d byte crypt key\n",crypt_len));
+  DEBUG(3,("Chose protocol [%s]\n",prots[SVAL(inbuf,smb_vwv0)].name));
+
+  doencrypt = ((sec_mode & 2) != 0);
+
+  if (servertime) {
+    static BOOL done_time = False;
+    if (!done_time) {
+      DEBUG(1,("Server time is %sTimezone is UTC%+02.1f\n",
+              asctime(LocalTime(&servertime,GMT_TO_LOCAL)),
+              -(double)(serverzone/3600.0)));
+      done_time = True;
+    }
+  }
+
+ get_pass:
+
+  if (got_pass)
+    pass = password;
+  else
+    pass = (char *)getpass("Password: ");
+
+  if (Protocol >= PROTOCOL_LANMAN1 && use_setup)
+    {
+      fstring pword;
+      int passlen = strlen(pass)+1;
+      strcpy(pword,pass);      
+
+#ifdef SMB_PASSWD
+      if (doencrypt && *pass) {
+       DEBUG(3,("Using encrypted passwords\n"));
+       passlen = 24;
+       SMBencrypt(pass,cryptkey,pword);
+      }
+#else
+      doencrypt = False;
+#endif
+
+      /* if in share level security then don't send a password now */
+      if (!(sec_mode & 1)) {strcpy(pword, "");passlen=1;} 
+
+      /* send a session setup command */
+      bzero(outbuf,smb_size);
+
+      if (Protocol < PROTOCOL_NT1) {
+       set_message(outbuf,10,1 + strlen(username) + passlen,True);
+       CVAL(outbuf,smb_com) = SMBsesssetupX;
+       setup_pkt(outbuf);
+
+       CVAL(outbuf,smb_vwv0) = 0xFF;
+       SSVAL(outbuf,smb_vwv2,max_xmit);
+       SSVAL(outbuf,smb_vwv3,2);
+       SSVAL(outbuf,smb_vwv4,max_vcs-1);
+       SIVAL(outbuf,smb_vwv5,sesskey);
+       SSVAL(outbuf,smb_vwv7,passlen);
+       p = smb_buf(outbuf);
+       memcpy(p,pword,passlen);
+       p += passlen;
+       strcpy(p,username);
+      } else {
+       if (!doencrypt) passlen--;
+       /* for Win95 */
+       set_message(outbuf,13,0,True);
+       CVAL(outbuf,smb_com) = SMBsesssetupX;
+       setup_pkt(outbuf);
+
+       CVAL(outbuf,smb_vwv0) = 0xFF;
+       SSVAL(outbuf,smb_vwv2,BUFFER_SIZE);
+       SSVAL(outbuf,smb_vwv3,2);
+       SSVAL(outbuf,smb_vwv4,getpid());
+       SIVAL(outbuf,smb_vwv5,sesskey);
+       SSVAL(outbuf,smb_vwv7,passlen);
+       SSVAL(outbuf,smb_vwv8,0);
+       p = smb_buf(outbuf);
+       memcpy(p,pword,passlen); p += SVAL(outbuf,smb_vwv7);
+       strcpy(p,username);p = skip_string(p,1);
+       strcpy(p,workgroup);p = skip_string(p,1);
+       strcpy(p,"Unix");p = skip_string(p,1);
+       strcpy(p,"Samba");p = skip_string(p,1);
+       set_message(outbuf,13,PTR_DIFF(p,smb_buf(outbuf)),False);
+      }
+
+      send_smb(Client,outbuf);
+      receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+      show_msg(inbuf);
+
+      if (CVAL(inbuf,smb_rcls) != 0)
+       {
+         if (! *pass &&
+             ((CVAL(inbuf,smb_rcls) == ERRDOS && 
+               SVAL(inbuf,smb_err) == ERRnoaccess) ||
+              (CVAL(inbuf,smb_rcls) == ERRSRV && 
+               SVAL(inbuf,smb_err) == ERRbadpw)))
+           {
+             got_pass = False;
+             DEBUG(3,("resending login\n"));
+             goto get_pass;
+           }
+             
+         DEBUG(0,("Session setup failed for username=%s myname=%s destname=%s   %s\n",
+               username,myname,desthost,smb_errstr(inbuf)));
+         DEBUG(0,("You might find the -U, -W or -n options useful\n"));
+         DEBUG(0,("Sometimes you have to use `-n USERNAME' (particularly with OS/2)\n"));
+         DEBUG(0,("Some servers also insist on uppercase-only passwords\n"));
+         if (was_null)
+           {
+             free(inbuf);
+             free(outbuf);
+           }
+         return(False);
+       }
+
+      if (Protocol >= PROTOCOL_NT1) {
+       char *domain,*os,*lanman;
+       p = smb_buf(inbuf);
+       os = p;
+       lanman = skip_string(os,1);
+       domain = skip_string(lanman,1);
+       if (*domain || *os || *lanman)
+         DEBUG(1,("Domain=[%s] OS=[%s] Server=[%s]\n",domain,os,lanman));
+      }
+
+      /* use the returned uid from now on */
+      if (SVAL(inbuf,smb_uid) != uid)
+       DEBUG(3,("Server gave us a UID of %d. We gave %d\n",
+             SVAL(inbuf,smb_uid),uid));
+      uid = SVAL(inbuf,smb_uid);
+    }
+
+  /* now we've got a connection - send a tcon message */
+  bzero(outbuf,smb_size);
+
+  if (strncmp(service,"\\\\",2) != 0)
+    {
+      DEBUG(0,("\nWarning: Your service name doesn't start with \\\\. This is probably incorrect.\n"));
+      DEBUG(0,("Perhaps try replacing each \\ with \\\\ on the command line?\n\n"));
+    }
+
+
+ again2:
+
+  {
+    int passlen = strlen(pass)+1;
+    fstring pword;
+    strcpy(pword,pass);
+
+#ifdef SMB_PASSWD
+    if (doencrypt && *pass) {
+      passlen=24;
+      SMBencrypt(pass,cryptkey,pword);      
+    }
+#endif
+
+    /* if in user level security then don't send a password now */
+    if ((sec_mode & 1)) {
+      strcpy(pword, ""); passlen=1; 
+    }
+
+    set_message(outbuf,4,2 + strlen(service) + passlen + strlen(dev),True);
+    CVAL(outbuf,smb_com) = SMBtconX;
+    setup_pkt(outbuf);
+
+    SSVAL(outbuf,smb_vwv0,0xFF);
+    SSVAL(outbuf,smb_vwv3,passlen);
+
+    p = smb_buf(outbuf);
+    memcpy(p,pword,passlen);
+    p += passlen;
+    strcpy(p,service);
+    p = skip_string(p,1);
+    strcpy(p,dev);
+  }
+
+  send_smb(Client,outbuf);
+  receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+  /* trying again with a blank password */
+  if (CVAL(inbuf,smb_rcls) != 0 && 
+      (int)strlen(pass) > 0 && 
+      !doencrypt &&
+      Protocol >= PROTOCOL_LANMAN1)
+    {
+      DEBUG(2,("first SMBtconX failed, trying again. %s\n",smb_errstr(inbuf)));
+      strcpy(pass,"");
+      goto again2;
+    }  
+
+  if (CVAL(inbuf,smb_rcls) != 0)
+    {
+      DEBUG(0,("SMBtconX failed. %s\n",smb_errstr(inbuf)));
+      DEBUG(0,("Perhaps you are using the wrong sharename, username or password?\n"));
+      DEBUG(0,("Some servers insist that these be in uppercase\n"));
+      if (was_null)
+       {
+         free(inbuf);
+         free(outbuf);
+       }
+      return(False);
+    }
+  
+
+  max_xmit = MIN(max_xmit,BUFFER_SIZE-4);
+  if (max_xmit <= 0)
+    max_xmit = BUFFER_SIZE - 4;
+
+  cnum = SVAL(inbuf,smb_tid);
+
+  DEBUG(3,("Connected with cnum=%d max_xmit=%d\n",cnum,max_xmit));
+
+  if (was_null)
+    {
+      free(inbuf);
+      free(outbuf);
+    }
+  return True;
+}
+
+
+/****************************************************************************
+send a logout command
+****************************************************************************/
+static void send_logout(void )
+{
+  pstring inbuf,outbuf;
+
+  bzero(outbuf,smb_size);
+  set_message(outbuf,0,0,True);
+  CVAL(outbuf,smb_com) = SMBtdis;
+  SSVAL(outbuf,smb_tid,cnum);
+  setup_pkt(outbuf);
+
+  send_smb(Client,outbuf);
+  receive_smb(Client,inbuf,SHORT_TIMEOUT);
+
+  if (CVAL(inbuf,smb_rcls) != 0)
+    {
+      DEBUG(0,("SMBtdis failed %s\n",smb_errstr(inbuf)));
+    }
+
+  
+#ifdef STATS
+  stats_report();
+#endif
+  exit(0);
+}
+
+
+
+/****************************************************************************
+call a remote api
+****************************************************************************/
+static BOOL call_api(int prcnt,int drcnt,
+                    int mprcnt,int mdrcnt,
+                    int *rprcnt,int *rdrcnt,
+                    char *param,char *data,
+                    char **rparam,char **rdata)
+{
+  static char *inbuf=NULL;
+  static char *outbuf=NULL;
+
+  if (!inbuf) inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+  if (!outbuf) outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+
+  send_trans_request(outbuf,SMBtrans,"\\PIPE\\LANMAN",0,0,
+                    data,param,NULL,
+                    drcnt,prcnt,0,
+                    mdrcnt,mprcnt,0);
+
+  return (receive_trans_response(inbuf,SMBtrans,
+                                 rdrcnt,rprcnt,
+                                 rdata,rparam));
+}
+
+/****************************************************************************
+  send a SMB trans or trans2 request
+  ****************************************************************************/
+static BOOL send_trans_request(char *outbuf,int trans,
+                              char *name,int fid,int flags,
+                              char *data,char *param,uint16 *setup,
+                              int ldata,int lparam,int lsetup,
+                              int mdata,int mparam,int msetup)
+{
+  int i;
+  int this_ldata,this_lparam;
+  int tot_data=0,tot_param=0;
+  char *outdata,*outparam;
+  pstring inbuf;
+  char *p;
+
+  this_lparam = MIN(lparam,max_xmit - (500+lsetup*SIZEOFWORD)); /* hack */
+  this_ldata = MIN(ldata,max_xmit - (500+lsetup*SIZEOFWORD+this_lparam));
+
+  bzero(outbuf,smb_size);
+  set_message(outbuf,14+lsetup,0,True);
+  CVAL(outbuf,smb_com) = trans;
+  SSVAL(outbuf,smb_tid,cnum);
+  setup_pkt(outbuf);
+
+  outparam = smb_buf(outbuf)+(trans==SMBtrans ? strlen(name)+1 : 3);
+  outdata = outparam+this_lparam;
+
+  /* primary request */
+  SSVAL(outbuf,smb_tpscnt,lparam);     /* tpscnt */
+  SSVAL(outbuf,smb_tdscnt,ldata);      /* tdscnt */
+  SSVAL(outbuf,smb_mprcnt,mparam);     /* mprcnt */
+  SSVAL(outbuf,smb_mdrcnt,mdata);      /* mdrcnt */
+  SCVAL(outbuf,smb_msrcnt,msetup);     /* msrcnt */
+  SSVAL(outbuf,smb_flags,flags);       /* flags */
+  SIVAL(outbuf,smb_timeout,0);         /* timeout */
+  SSVAL(outbuf,smb_pscnt,this_lparam); /* pscnt */
+  SSVAL(outbuf,smb_psoff,smb_offset(outparam,outbuf)); /* psoff */
+  SSVAL(outbuf,smb_dscnt,this_ldata);  /* dscnt */
+  SSVAL(outbuf,smb_dsoff,smb_offset(outdata,outbuf)); /* dsoff */
+  SCVAL(outbuf,smb_suwcnt,lsetup);     /* suwcnt */
+  for (i=0;i<lsetup;i++)               /* setup[] */
+    SSVAL(outbuf,smb_setup+i*SIZEOFWORD,setup[i]);
+  p = smb_buf(outbuf);
+  if (trans==SMBtrans)
+    strcpy(p,name);                    /* name[] */
+  else
+    {
+      *p++ = 0;                                /* put in a null smb_name */
+      *p++ = 'D'; *p++ = ' ';          /* this was added because OS/2 does it */
+    }
+  if (this_lparam)                     /* param[] */
+    memcpy(outparam,param,this_lparam);
+  if (this_ldata)                      /* data[] */
+    memcpy(outdata,data,this_ldata);
+  set_message(outbuf,14+lsetup,                /* wcnt, bcc */
+             PTR_DIFF(outdata+this_ldata,smb_buf(outbuf)),False);
+
+  show_msg(outbuf);
+  send_smb(Client,outbuf);
+
+  if (this_ldata < ldata || this_lparam < lparam)
+    {
+      /* receive interim response */
+      if (!receive_smb(Client,inbuf,SHORT_TIMEOUT) || CVAL(inbuf,smb_rcls) != 0)
+       {
+         DEBUG(0,("%s request failed (%s)\n",
+                  trans==SMBtrans?"SMBtrans":"SMBtrans2", smb_errstr(inbuf)));
+         return(False);
+       }      
+
+      tot_data = this_ldata;
+      tot_param = this_lparam;
+
+      while (tot_data < ldata || tot_param < lparam)
+    {
+         this_lparam = MIN(lparam-tot_param,max_xmit - 500); /* hack */
+         this_ldata = MIN(ldata-tot_data,max_xmit - (500+this_lparam));
+
+         set_message(outbuf,trans==SMBtrans?8:9,0,True);
+         CVAL(outbuf,smb_com) = trans==SMBtrans ? SMBtranss : SMBtranss2;
+
+         outparam = smb_buf(outbuf);
+         outdata = outparam+this_lparam;
+
+         /* secondary request */
+         SSVAL(outbuf,smb_tpscnt,lparam);      /* tpscnt */
+         SSVAL(outbuf,smb_tdscnt,ldata);       /* tdscnt */
+         SSVAL(outbuf,smb_spscnt,this_lparam); /* pscnt */
+         SSVAL(outbuf,smb_spsoff,smb_offset(outparam,outbuf)); /* psoff */
+         SSVAL(outbuf,smb_spsdisp,tot_param);  /* psdisp */
+         SSVAL(outbuf,smb_sdscnt,this_ldata);  /* dscnt */
+         SSVAL(outbuf,smb_sdsoff,smb_offset(outdata,outbuf)); /* dsoff */
+         SSVAL(outbuf,smb_sdsdisp,tot_data);   /* dsdisp */
+         if (trans==SMBtrans2)
+           SSVAL(outbuf,smb_sfid,fid);         /* fid */
+         if (this_lparam)                      /* param[] */
+           memcpy(outparam,param,this_lparam);
+         if (this_ldata)                       /* data[] */
+           memcpy(outdata,data,this_ldata);
+         set_message(outbuf,trans==SMBtrans?8:9, /* wcnt, bcc */
+                     PTR_DIFF(outdata+this_ldata,smb_buf(outbuf)),False);
+
+         show_msg(outbuf);
+         send_smb(Client,outbuf);
+
+         tot_data += this_ldata;
+         tot_param += this_lparam;
+       }
+    }
+
+    return(True);
+}
+
+/****************************************************************************
+try and browse available connections on a host
+****************************************************************************/
+static BOOL browse_host(BOOL sort)
+{
+#ifdef NOSTRCASECMP
+#define strcasecmp StrCaseCmp
+#endif
+  extern int strcasecmp();
+
+  char *rparam = NULL;
+  char *rdata = NULL;
+  char *p;
+  int rdrcnt,rprcnt;
+  pstring param;
+  int count = -1;
+
+  /* now send a SMBtrans command with api RNetShareEnum */
+  p = param;
+  SSVAL(p,0,0); /* api number */
+  p += 2;
+  strcpy(p,"WrLeh");
+  p = skip_string(p,1);
+  strcpy(p,"B13BWz");
+  p = skip_string(p,1);
+  SSVAL(p,0,1);
+  SSVAL(p,2,BUFFER_SIZE);
+  p += 4;
+
+  if (call_api(PTR_DIFF(p,param),0,
+              1024,BUFFER_SIZE,
+               &rprcnt,&rdrcnt,
+              param,NULL,
+              &rparam,&rdata))
+    {
+      int res = SVAL(rparam,0);
+      int converter=SVAL(rparam,2);
+      int i;
+      BOOL long_share_name=False;
+      
+      if (res == 0)
+       {
+         count=SVAL(rparam,4);
+         p = rdata;
+
+         if (count > 0)
+           {
+             printf("\n\tSharename      Type      Comment\n");
+             printf("\t---------      ----      -------\n");
+           }
+
+         if (sort)
+           qsort(p,count,20,QSORT_CAST strcasecmp);
+
+         for (i=0;i<count;i++)
+           {
+             char *sname = p;
+             int type = SVAL(p,14);
+             int comment_offset = IVAL(p,16) & 0xFFFF;
+             fstring typestr;
+             *typestr=0;
+
+             switch (type)
+               {
+               case STYPE_DISKTREE:
+                 strcpy(typestr,"Disk"); break;
+               case STYPE_PRINTQ:
+                 strcpy(typestr,"Printer"); break;           
+               case STYPE_DEVICE:
+                 strcpy(typestr,"Device"); break;
+               case STYPE_IPC:
+                 strcpy(typestr,"IPC"); break;      
+               }
+
+             printf("\t%-15.15s%-10.10s%s\n",
+                    sname,
+                    typestr,
+                    comment_offset?rdata+comment_offset-converter:"");
+         
+             if (strlen(sname)>8) long_share_name=True;
+         
+             p += 20;
+           }
+
+         if (long_share_name) {
+           printf("\nNOTE: There were share names longer than 8 chars.\nOn older clients these may not be accessible or may give browsing errors\n");
+         }
+       }
+    }
+  
+  if (rparam) free(rparam);
+  if (rdata) free(rdata);
+
+  return(count>0);
+}
+
+
+/****************************************************************************
+get some server info
+****************************************************************************/
+static void server_info()
+{
+  char *rparam = NULL;
+  char *rdata = NULL;
+  char *p;
+  int rdrcnt,rprcnt;
+  pstring param;
+
+  bzero(param,sizeof(param));
+
+  p = param;
+  SSVAL(p,0,63); /* api number */
+  p += 2;
+  strcpy(p,"WrLh");
+  p = skip_string(p,1);
+  strcpy(p,"zzzBBzz");
+  p = skip_string(p,1);
+  SSVAL(p,0,10); /* level 10 */
+  SSVAL(p,2,1000);
+  p += 6;
+
+  if (call_api(PTR_DIFF(p,param),0,
+              6,1000,
+              &rprcnt,&rdrcnt,
+              param,NULL,
+              &rparam,&rdata))
+    {
+      int res = SVAL(rparam,0);
+      int converter=SVAL(rparam,2);
+
+      if (res == 0)
+       {
+      p = rdata;
+
+      printf("\nServer=[%s] User=[%s] Workgroup=[%s] Domain=[%s]\n",
+            rdata+SVAL(p,0)-converter,
+            rdata+SVAL(p,4)-converter,
+            rdata+SVAL(p,8)-converter,
+            rdata+SVAL(p,14)-converter);
+    }
+    }
+
+  if (rparam) free(rparam);
+  if (rdata) free(rdata);
+
+  return;
+}
+
+
+/****************************************************************************
+try and browse available connections on a host
+****************************************************************************/
+static BOOL list_servers()
+{
+  char *rparam = NULL;
+  char *rdata = NULL;
+  int rdrcnt,rprcnt;
+  char *p;
+  pstring param;
+  int uLevel = 1;
+  int count = 0;
+
+  /* now send a SMBtrans command with api ServerEnum? */
+  p = param;
+  SSVAL(p,0,0x68); /* api number */
+  p += 2;
+  strcpy(p,"WrLehDO");
+  p = skip_string(p,1);
+
+  strcpy(p,"B16BBDz");
+#if 0
+  strcpy(p,getenv("XX_STR2"));
+#endif
+
+  p = skip_string(p,1);
+  SSVAL(p,0,uLevel);
+  SSVAL(p,2,0x2000); /* buf length */
+  p += 4;
+
+  SIVAL(p,0,SV_TYPE_ALL);
+
+  if (call_api(PTR_DIFF(p+4,param),0,
+              8,10000,
+              &rprcnt,&rdrcnt,
+              param,NULL,
+              &rparam,&rdata))
+    {
+      int res = SVAL(rparam,0);
+      int converter=SVAL(rparam,2);
+      int i;
+
+      if (res == 0) {  
+       char *p2 = rdata;
+       count=SVAL(rparam,4);
+
+       if (count > 0) {
+         printf("\n\nThis machine has a browse list:\n");
+         printf("\n\tServer               Comment\n");
+         printf("\t---------            -------\n");
+       }
+       
+       for (i=0;i<count;i++) {
+         char *sname = p2;
+         int comment_offset = IVAL(p2,22) & 0xFFFF;
+         printf("\t%-16.16s     %s\n",
+                sname,
+                comment_offset?rdata+comment_offset-converter:"");
+         
+         p2 += 26;
+       }
+      }
+    }
+
+  if (rparam) {free(rparam); rparam = NULL;}
+  if (rdata) {free(rdata); rdata = NULL;}
+
+  SIVAL(p,0,SV_TYPE_DOMAIN_ENUM);
+
+  if (call_api(PTR_DIFF(p+4,param),0,
+              8,10000,
+              &rprcnt,&rdrcnt,
+              param,NULL,
+              &rparam,&rdata))
+    {
+      int res = SVAL(rparam,0);
+      int converter=SVAL(rparam,2);
+      int i;
+
+      if (res == 0) {
+       char *p2 = rdata;
+       count=SVAL(rparam,4);
+
+       if (count > 0) {
+         printf("\n\nThis machine has a workgroup list:\n");
+         printf("\n\tWorkgroup            Master\n");
+         printf("\t---------            -------\n");
+       }
+       
+       for (i=0;i<count;i++) {
+         char *sname = p2;
+         int comment_offset = IVAL(p2,22) & 0xFFFF;
+         printf("\t%-16.16s     %s\n",
+                sname,
+                comment_offset?rdata+comment_offset-converter:"");
+         
+         p2 += 26;
+       }
+      }
+    }
+
+  if (rparam) free(rparam);
+  if (rdata) free(rdata);
+
+  return(count>0);
+}
+
+
+
+
+void cmd_help();
+
+/* This defines the commands supported by this client */
+struct
+{
+  char *name;
+  void (*fn)();
+  char *description;
+} commands[] = 
+{
+  {"ls",cmd_dir,"<mask> list the contents of the current directory"},
+  {"dir",cmd_dir,"<mask> list the contents of the current directory"},
+  {"lcd",cmd_lcd,"[directory] change/report the local current working directory"},
+  {"cd",cmd_cd,"[directory] change/report the remote directory"},
+  {"pwd",cmd_pwd,"show current remote directory (same as 'cd' with no args)"},
+  {"get",cmd_get,"<remote name> [local name] get a file"},
+  {"mget",cmd_mget,"<mask> get all the matching files"},
+  {"put",cmd_put,"<local name> [remote name] put a file"},
+  {"mput",cmd_mput,"<mask> put all matching files"},
+  {"rename",cmd_rename,"<src> <dest> rename some files"},
+  {"more",cmd_more,"<remote name> view a remote file with your pager"},  
+  {"mask",cmd_select,"<mask> mask all filenames against this"},
+  {"del",cmd_del,"<mask> delete all matching files"},
+  {"rm",cmd_del,"<mask> delete all matching files"},
+  {"mkdir",cmd_mkdir,"<directory> make a directory"},
+  {"md",cmd_mkdir,"<directory> make a directory"},
+  {"rmdir",cmd_rmdir,"<directory> remove a directory"},
+  {"rd",cmd_rmdir,"<directory> remove a directory"},
+  {"prompt",cmd_prompt,"toggle prompting for filenames for mget and mput"},  
+  {"recurse",cmd_recurse,"toggle directory recursion for mget and mput"},  
+  {"translate",cmd_translate,"toggle text translation for printing"},  
+  {"lowercase",cmd_lowercase,"toggle lowercasing of filenames for get"},  
+  {"print",cmd_print,"<file name> print a file"},
+  {"printmode",cmd_printmode,"<graphics or text> set the print mode"},
+  {"queue",cmd_queue,"show the print queue"},
+  {"cancel",cmd_cancel,"<jobid> cancel a print queue entry"},
+  {"stat",cmd_stat,"<file> get info on a file (experimental!)"},
+  {"quit",send_logout,"logoff the server"},
+  {"q",send_logout,"logoff the server"},
+  {"exit",send_logout,"logoff the server"},
+  {"newer",cmd_newer,"<file> only mget files newer than the specified local file"},
+  {"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"},
+  {"tar",cmd_tar,"tar <c|x>[IXbgNa] current directory to/from <file name>" },
+  {"blocksize",cmd_block,"blocksize <number> (default 20)" },
+  {"tarmode",cmd_tarmode,
+     "<full|inc|reset|noreset> tar's behaviour towards archive bits" },
+  {"setmode",cmd_setmode,"filename <setmode string> change modes of file"},
+  {"help",cmd_help,"[command] give help on a command"},
+  {"?",cmd_help,"[command] give help on a command"},
+  {"!",NULL,"run a shell command on the local system"},
+  {"",NULL,NULL}
+};
+
+
+/*******************************************************************
+  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+1))
+       {
+         matches++;
+         cmd = i;
+       }
+      i++;
+    }
+  
+  if (matches == 0)
+    return(-1);
+  else if (matches == 1)
+    return(cmd);
+  else
+    return(-2);
+}
+
+/****************************************************************************
+help
+****************************************************************************/
+void cmd_help(void)
+{
+  int i=0,j;
+  fstring buf;
+
+  if (next_token(NULL,buf,NULL))
+    {
+      if ((i = process_tok(buf)) >= 0)
+       DEBUG(0,("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++) {
+         DEBUG(0,("%-15s",commands[i].name));
+         i++;
+       }
+       DEBUG(0,("\n"));
+      }
+}
+
+/****************************************************************************
+open the client sockets
+****************************************************************************/
+static BOOL open_sockets(int port )
+{
+  static int last_port;
+  char *host;
+  pstring service2;
+  extern int Client;
+#ifdef USENMB
+  BOOL failed = True;
+#endif
+
+  if (port == 0) port=last_port;
+  last_port=port;
+
+  strupper(service);
+
+  if (*desthost)
+    {
+      host = desthost;
+    }
+  else
+    {
+      strcpy(service2,service);
+      host = strtok(service2,"\\/");
+      if (!host) {
+       DEBUG(0,("Badly formed host name\n"));
+       return(False);
+      }
+      strcpy(desthost,host);
+    }
+
+  DEBUG(3,("Opening sockets\n"));
+
+  if (*myname == 0)
+    {
+      get_myname(myname,NULL);
+      strupper(myname);
+    }
+
+  if (!have_ip)
+    {
+      struct hostent *hp;
+
+      if ((hp = Get_Hostbyname(host))) {
+       putip((char *)&dest_ip,(char *)hp->h_addr);
+       failed = False;
+      } else {
+#ifdef USENMB
+       /* Try and resolve the name with the netbios server */
+       int             bcast;
+       pstring         hs;
+       struct in_addr  ip1, ip2;
+       
+       if ((bcast = open_socket_in(SOCK_DGRAM, 0, 3)) != -1) {
+         set_socket_options (bcast, "SO_BROADCAST");
+
+         if (!got_bcast && get_myname(hs, &ip1)) {
+           get_broadcast(&ip1, &bcast_ip, &ip2);
+         }
+
+         if (name_query(bcast, host, 0x20, True, True, bcast_ip, &dest_ip,0)){
+           failed = False;
+         }
+         close (bcast);
+       }
+#endif
+       if (failed) {
+         DEBUG(0,("Get_Hostbyname: Unknown host %s.\n",host));
+         return False;
+       }
+      }
+    }
+
+  Client = open_socket_out(SOCK_STREAM, &dest_ip, port);
+  if (Client == -1)
+    return False;
+
+  DEBUG(3,("Connected\n"));
+  
+  set_socket_options(Client,user_socket_options);  
+  
+  return True;
+}
+
+/****************************************************************************
+wait for keyboard activity, swallowing network packets
+****************************************************************************/
+#ifdef CLIX
+static char wait_keyboard(char *buffer)
+#else
+static void wait_keyboard(char *buffer)
+#endif
+{
+  fd_set fds;
+  int selrtn;
+  struct timeval timeout;
+  
+#ifdef CLIX
+  int delay = 0;
+#endif
+  
+  while (1) 
+    {
+      extern int Client;
+      FD_ZERO(&fds);
+      FD_SET(Client,&fds);
+#ifndef CLIX
+      FD_SET(fileno(stdin),&fds);
+#endif
+
+      timeout.tv_sec = 20;
+      timeout.tv_usec = 0;
+#ifdef CLIX
+      timeout.tv_sec = 0;
+#endif
+      selrtn = sys_select(&fds,&timeout);
+      
+#ifndef CLIX
+      if (FD_ISSET(fileno(stdin),&fds))
+       return;
+#else
+      {
+       char ch;
+       int f_flags;
+       int readret;
+       
+       f_flags = fcntl(fileno(stdin), F_GETFL, 0);
+       fcntl( fileno(stdin), F_SETFL, f_flags | O_NONBLOCK);
+       readret = read_data( fileno(stdin), &ch, 1);
+       fcntl(fileno(stdin), F_SETFL, f_flags);
+       if (readret == -1)
+         {
+           if (errno != EAGAIN)
+             {
+               /* should crash here */
+               DEBUG(1,("readchar stdin failed\n"));
+             }
+         }
+       else if (readret != 0)
+         {
+           return ch;
+         }
+      }
+#endif
+      if (FD_ISSET(Client,&fds))
+       receive_smb(Client,buffer,0);
+      
+#ifdef CLIX
+      delay++;
+      if (delay > 100000)
+       {
+         delay = 0;
+         chkpath("\\",False);
+       }
+#else
+      chkpath("\\",False);
+#endif
+    }  
+}
+
+
+/****************************************************************************
+close and open the connection again
+****************************************************************************/
+BOOL reopen_connection(char *inbuf,char *outbuf)
+{
+  static int open_count=0;
+
+  open_count++;
+
+  if (open_count>5) return(False);
+
+  DEBUG(1,("Trying to re-open connection\n"));
+
+  set_message(outbuf,0,0,True);
+  SCVAL(outbuf,smb_com,SMBtdis);
+  SSVAL(outbuf,smb_tid,cnum);
+  setup_pkt(outbuf);
+
+  send_smb(Client,outbuf);
+  receive_smb(Client,inbuf,SHORT_TIMEOUT);
+
+  close_sockets();
+  if (!open_sockets(0)) return(False);
+
+  return(send_login(inbuf,outbuf,True,True));
+}
+
+/****************************************************************************
+  process commands from the client
+****************************************************************************/
+BOOL process(char *base_directory)
+{
+  extern FILE *dbf;
+  pstring line;
+
+  char *InBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+  char *OutBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+
+  if ((InBuffer == NULL) || (OutBuffer == NULL)) 
+    return(False);
+  
+  bzero(OutBuffer,smb_size);
+
+  if (!send_login(InBuffer,OutBuffer,True,True))
+    return(False);
+
+  if (*base_directory) do_cd(base_directory);
+
+  while (!feof(stdin))
+    {
+      fstring tok;
+      int i;
+
+      bzero(OutBuffer,smb_size);
+
+      /* display a prompt */
+      DEBUG(1,("smb: %s> ", CNV_LANG(cur_dir)));
+      fflush(dbf);
+
+#ifdef CLIX
+      line[0] = wait_keyboard(InBuffer);
+      /* this might not be such a good idea... */
+      if ( line[0] == EOF)
+       break;
+#else
+      wait_keyboard(InBuffer);
+#endif
+  
+      /* and get a response */
+#ifdef CLIX
+      fgets( &line[1],999, stdin);
+#else
+      if (!fgets(line,1000,stdin))
+       break;
+#endif
+
+      /* input language code to internal one */
+      CNV_INPUT (line);
+
+      /* special case - first char is ! */
+      if (*line == '!')
+       {
+         system(line + 1);
+         continue;
+       }
+      
+      /* and get the first part of the command */
+      {
+       char *ptr = line;
+       if (!next_token(&ptr,tok,NULL)) continue;
+      }
+
+      if ((i = process_tok(tok)) >= 0)
+       commands[i].fn(InBuffer,OutBuffer);
+      else if (i == -2)
+       DEBUG(0,("%s: command abbreviation ambiguous\n",CNV_LANG(tok)));
+      else
+       DEBUG(0,("%s: command not found\n",CNV_LANG(tok)));
+    }
+  
+  send_logout();
+  return(True);
+}
+
+
+/****************************************************************************
+usage on the program
+****************************************************************************/
+void usage(char *pname)
+{
+  DEBUG(0,("Usage: %s service <password> [-p port] [-d debuglevel] [-l log] ",
+          pname));
+
+#ifdef KANJI
+  DEBUG(0,("[-t termcode] "));
+#endif /* KANJI */
+
+  DEBUG(0,("\nVersion %s\n",VERSION));
+  DEBUG(0,("\t-p port               listen on the specified port\n"));
+  DEBUG(0,("\t-d debuglevel         set the debuglevel\n"));
+  DEBUG(0,("\t-l log basename.      Basename for log/debug files\n"));
+  DEBUG(0,("\t-n netbios name.      Use this name as my netbios name\n"));
+  DEBUG(0,("\t-N                    don't ask for a password\n"));
+  DEBUG(0,("\t-P                    connect to service as a printer\n"));
+  DEBUG(0,("\t-M host               send a winpopup message to the host\n"));
+  DEBUG(0,("\t-m max protocol       set the max protocol level\n"));
+  DEBUG(0,("\t-L host               get a list of shares available on a host\n"));
+  DEBUG(0,("\t-I dest IP            use this IP to connect to\n"));
+  DEBUG(0,("\t-E                    write messages to stderr instead of stdout\n"));
+  DEBUG(0,("\t-U username           set the network username\n"));
+  DEBUG(0,("\t-W workgroup          set the workgroup name\n"));
+#ifdef KANJI
+  DEBUG(0,("\t-t terminal code      terminal i/o code {sjis|euc|jis7|jis8|junet|hex}\n"));
+#endif /* KANJI */
+  DEBUG(0,("\t-T<c|x>IXgbNa          command line tar\n"));
+  DEBUG(0,("\t-D directory          start from directory\n"));
+  DEBUG(0,("\n"));
+}
+
+
+
+/****************************************************************************
+  main program
+****************************************************************************/
+int main(int argc,char *argv[])
+{
+  fstring base_directory;
+  char *pname = argv[0];
+  int port = 139;
+  int opt;
+  extern FILE *dbf;
+  extern char *optarg;
+  extern int optind;
+  pstring query_host;
+  BOOL message = False;
+  extern char tar_type;
+
+  *query_host = 0;
+  *base_directory = 0;
+
+  DEBUGLEVEL = 2;
+
+  setup_logging(pname,True);
+
+  TimeInit();
+  charset_initialise();
+
+  pid = getpid();
+  uid = getuid();
+  gid = getgid();
+  mid = pid + 100;
+  myumask = umask(0);
+  umask(myumask);
+
+  if (getenv("USER"))
+    {
+      strcpy(username,getenv("USER"));
+      strupper(username);
+    }
+
+  if (*username == 0 && getenv("LOGNAME"))
+    {
+      strcpy(username,getenv("LOGNAME"));
+      strupper(username);
+    }
+
+  if (argc < 2)
+    {
+      usage(pname);
+      exit(1);
+    }
+  
+  if (*argv[1] != '-')
+    {
+
+      strcpy(service,argv[1]);  
+      argc--;
+      argv++;
+
+      if (count_chars(service,'\\') < 3)
+       {
+         usage(pname);
+         printf("\n%s: Not enough '\\' characters in service\n",service);
+         exit(1);
+       }
+
+/*
+      if (count_chars(service,'\\') > 3)
+       {
+         usage(pname);
+         printf("\n%s: Too many '\\' characters in service\n",service);
+         exit(1);
+       }
+       */
+
+      if (argc > 1 && (*argv[1] != '-'))
+       {
+         got_pass = True;
+         strcpy(password,argv[1]);  
+         memset(argv[1],'X',strlen(argv[1]));
+         argc--;
+         argv++;
+       }
+    }
+
+#ifdef KANJI
+  setup_term_code (KANJI);
+  while ((opt = getopt (argc, argv, "B:O:M:i:Nn:d:Pp:l:hI:EB:U:L:t:m:W:T:D:")) != EOF)
+#else
+  while ((opt = getopt (argc, argv, "B:O:M:i:Nn:d:Pp:l:hI:EB:U:L:m:W:T:D:")) != EOF)
+#endif /* KANJI */
+    switch (opt)
+      {
+      case 'm':
+       max_protocol = interpret_protocol(optarg,max_protocol);
+       break;
+      case 'O':
+       strcpy(user_socket_options,optarg);
+       break;  
+      case 'M':
+       name_type = 3;
+       strcpy(desthost,optarg);
+       strupper(desthost);
+       message = True;
+       break;
+      case 'B':
+       bcast_ip = *interpret_addr2(optarg);
+       got_bcast = True;
+       break;
+      case 'D':
+       strcpy(base_directory,optarg);
+       break;
+      case 'T':
+       if (!tar_parseargs(argc, argv, optarg, optind)) {
+         usage(pname);
+         exit(1);
+       }
+       break;
+      case 'i':
+       strcpy(scope,optarg);
+       break;
+      case 'L':
+       got_pass = True;
+       strcpy(query_host,optarg);
+       break;
+      case 'U':
+       {
+         char *p;
+       strcpy(username,optarg);
+       if ((p=strchr(username,'%')))
+         {
+           *p = 0;
+           strcpy(password,p+1);
+           got_pass = True;
+           memset(strchr(optarg,'%')+1,'X',strlen(password));
+         }
+       }
+           
+       break;
+      case 'W':
+       strcpy(workgroup,optarg);
+       break;
+      case 'E':
+       dbf = stderr;
+       break;
+      case 'I':
+       {
+         dest_ip = *interpret_addr2(optarg);
+         if (zero_ip(dest_ip)) exit(1);
+         have_ip = True;
+       }
+       break;
+      case 'n':
+       strcpy(myname,optarg);
+       break;
+      case 'N':
+       got_pass = True;
+       break;
+      case 'P':
+       connect_as_printer = True;
+       break;
+      case 'd':
+       if (*optarg == 'A')
+         DEBUGLEVEL = 10000;
+       else
+         DEBUGLEVEL = atoi(optarg);
+       break;
+      case 'l':
+       sprintf(debugf,"%s.client",optarg);
+       break;
+      case 'p':
+       port = atoi(optarg);
+       break;
+      case 'h':
+       usage(pname);
+       exit(0);
+       break;
+#ifdef KANJI
+      case 't':
+       if (!setup_term_code (optarg)) {
+           DEBUG(0, ("%s: unknown terminal code name\n", optarg));
+           usage (pname);
+           exit (1);
+       }
+       break;
+#endif /* KANJI */
+      default:
+       usage(pname);
+       exit(1);
+      }
+
+  if (!tar_type && !*query_host && !*service && !message)
+    {
+      usage(pname);
+      exit(1);
+    }
+
+
+  DEBUG(3,("%s client started (version %s)\n",timestring(),VERSION));
+
+  get_myname(*myname?NULL:myname,&myip);  
+  strupper(myname);
+
+  if (tar_type) {
+    recurse=True;
+
+    if (open_sockets(port)) {
+        char *InBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+       char *OutBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+       int ret;
+
+       if ((InBuffer == NULL) || (OutBuffer == NULL)) 
+         return(1);
+
+       bzero(OutBuffer,smb_size);
+       if (!send_login(InBuffer,OutBuffer,True,True))
+         return(False);
+
+       if (*base_directory) do_cd(base_directory);
+
+       ret=process_tar(InBuffer, OutBuffer);
+
+       send_logout();
+       close_sockets();
+       return(ret);
+    } else
+      return(1);
+  }
+  
+  if (*query_host)
+    {
+      int ret = 0;
+      sprintf(service,"\\\\%s\\IPC$",query_host);
+      strupper(service);
+      connect_as_ipc = True;
+      if (open_sockets(port))
+       {
+#if 0
+         *username = 0;
+#endif
+         if (!send_login(NULL,NULL,True,True))
+           return(1);
+
+         server_info();
+         if (!browse_host(True)) {
+           sleep(1);
+           browse_host(True);
+         }
+         if (!list_servers()) {
+           sleep(1);
+           list_servers();
+         }
+
+         send_logout();
+         close_sockets();
+       }
+
+      return(ret);
+    }
+
+  if (message)
+    {
+      int ret = 0;
+      if (open_sockets(port))
+       {
+         pstring inbuf,outbuf;
+         bzero(outbuf,smb_size);
+         if (!send_session_request(inbuf,outbuf))
+           return(1);
+
+         send_message(inbuf,outbuf);
+
+         close_sockets();
+       }
+
+      return(ret);
+    }
+
+  if (open_sockets(port))
+    {
+      if (!process(base_directory))
+       {
+         close_sockets();
+         return(1);
+       }
+      close_sockets();
+    }
+  else
+    return(1);
+
+  return(0);
+}
+
+
+/* error code stuff - put together by Merik Karman
+   merik@blackadder.dsh.oz.au */
+
+typedef struct
+{
+  char *name;
+  int code;
+  char *message;
+} err_code_struct;
+
+/* Dos Error Messages */
+err_code_struct dos_msgs[] = {
+  {"ERRbadfunc",1,"Invalid function."},
+  {"ERRbadfile",2,"File not found."},
+  {"ERRbadpath",3,"Directory invalid."},
+  {"ERRnofids",4,"No file descriptors available"},
+  {"ERRnoaccess",5,"Access denied."},
+  {"ERRbadfid",6,"Invalid file handle."},
+  {"ERRbadmcb",7,"Memory control blocks destroyed."},
+  {"ERRnomem",8,"Insufficient server memory to perform the requested function."},
+  {"ERRbadmem",9,"Invalid memory block address."},
+  {"ERRbadenv",10,"Invalid environment."},
+  {"ERRbadformat",11,"Invalid format."},
+  {"ERRbadaccess",12,"Invalid open mode."},
+  {"ERRbaddata",13,"Invalid data."},
+  {"ERR",14,"reserved."},
+  {"ERRbaddrive",15,"Invalid drive specified."},
+  {"ERRremcd",16,"A Delete Directory request attempted  to  remove  the  server's  current directory."},
+  {"ERRdiffdevice",17,"Not same device."},
+  {"ERRnofiles",18,"A File Search command can find no more files matching the specified criteria."},
+  {"ERRbadshare",32,"The sharing mode specified for an Open conflicts with existing  FIDs  on the file."},
+  {"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."},
+  {"ERRfilexists",80,"The file named in a Create Directory, Make  New  File  or  Link  request already exists."},
+  {"ERRbadpipe",230,"Pipe invalid."},
+  {"ERRpipebusy",231,"All instances of the requested pipe are busy."},
+  {"ERRpipeclosing",232,"Pipe close in progress."},
+  {"ERRnotconnected",233,"No process on other end of pipe."},
+  {"ERRmoredata",234,"There is more data to be returned."},
+  {"ERRinvgroup",2455,"Invalid workgroup (try the -W option)"},
+  {NULL,-1,NULL}};
+
+/* Server Error Messages */
+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 */
+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,"A 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}};
+
+
+struct
+{
+  int code;
+  char *class;
+  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 SMB error string from a SMB buffer
+****************************************************************************/
+char *smb_errstr(char *inbuf)
+{
+  static pstring ret;
+  int class = CVAL(inbuf,smb_rcls);
+  int num = SVAL(inbuf,smb_err);
+  int i,j;
+
+  for (i=0;err_classes[i].class;i++)
+    if (err_classes[i].code == class)
+      {
+       if (err_classes[i].err_msgs)
+         {
+           err_code_struct *err = err_classes[i].err_msgs;
+           for (j=0;err[j].name;j++)
+             if (num == err[j].code)
+               {
+                 if (DEBUGLEVEL > 0)
+                   sprintf(ret,"%s - %s (%s)",err_classes[i].class,
+                           err[j].name,err[j].message);
+                 else
+                   sprintf(ret,"%s - %s",err_classes[i].class,err[j].name);
+                 return ret;
+               }
+         }
+
+       sprintf(ret,"%s - %d",err_classes[i].class,num);
+       return ret;
+      }
+  
+  sprintf(ret,"ERROR: Unknown error (%d,%d)",class,num);
+  return(ret);
+}
diff --git a/source/client/clitar.c b/source/client/clitar.c
new file mode 100644 (file)
index 0000000..1433ec5
--- /dev/null
@@ -0,0 +1,1713 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   Tar Extensions
+   Copyright (C) Ricky Poulten 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"
+#include "clitar.h"
+
+extern void setup_pkt(char *outbuf);
+extern BOOL reopen_connection(char *inbuf,char *outbuf);
+extern void do_dir(char *inbuf,char *outbuf,char *Mask,int attribute,void (*fn)(),BOOL recurse_dir);
+
+int tar_parseargs(int argc, char *argv[], char *Optarg, int Optind);
+
+extern BOOL recurse;
+
+#define SEPARATORS " \t\n\r"
+extern int DEBUGLEVEL;
+extern int Client;
+
+/* 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 int attribute = aDIR | aSYSTEM | aHIDDEN;
+
+#ifndef CLIENT_TIMEOUT
+#define CLIENT_TIMEOUT (30*1000)
+#endif
+
+static char *tarbuf;
+static int tp, ntarf, tbufsiz;
+/* Incremental mode */
+BOOL tar_inc=False;
+/* Reset archive bit */
+BOOL tar_reset=False;
+/* Include / exclude mode (true=include, false=exclude) */
+BOOL tar_excl=True;
+char tar_type='\0';
+static char **cliplist=NULL;
+static int clipn=0;
+
+extern file_info def_finfo;
+extern BOOL lowercase;
+extern int cnum;
+extern BOOL readbraw_supported;
+extern int max_xmit;
+extern pstring cur_dir;
+extern int get_total_time_ms;
+extern int get_total_size;
+extern int Protocol;
+
+int blocksize=20;
+int tarhandle;
+
+static void writetarheader();
+static void do_atar();
+static void do_tar();
+static void oct_it();
+static void fixtarname();
+static int dotarbuf();
+static void dozerobuf();
+static void dotareof();
+static void initarbuf();
+static int do_setrattr();
+void cmd_tar();
+int process_tar();
+char **toktocliplist();
+int clipfind();
+/* restore functions */
+static long readtarheader();
+static long unoct();
+static void do_tarput();
+static void unfixtarname();
+
+/*
+ * tar specific utitlities
+ */
+
+/****************************************************************************
+Write a tar header to buffer
+****************************************************************************/
+static void writetarheader(int f,  char *aname, int size, time_t mtime,
+                   char *amode)
+{
+  union hblock hb;
+  int i, chk, l;
+  char *jp;
+
+  memset(hb.dummy, 0, sizeof(hb.dummy));
+  
+  l=strlen(aname);
+  if (l >= NAMSIZ)
+    {
+      DEBUG(0, ("tar file %s name length exceeds NAMSIZ\n", aname));
+    }
+
+  /* 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';
+  strcpy(hb.dbuf.mode, amode);
+  oct_it(0L, 8, hb.dbuf.uid);
+  oct_it(0L, 8, hb.dbuf.gid);
+  oct_it((long) size, 13, hb.dbuf.size);
+  oct_it((long) mtime, 13, hb.dbuf.mtime);
+  memcpy(hb.dbuf.chksum, "        ", sizeof(hb.dbuf.chksum));
+  hb.dbuf.linkflag='0';
+  memset(hb.dbuf.linkname, 0, NAMSIZ);
+  
+  for (chk=0, i=sizeof(hb.dummy), jp=hb.dummy; --i>=0;) chk+=(0xFF & *jp++);
+
+  oct_it((long) 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_info *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=%d fchk=%d chksum=%s\n",
+           chk, fchk, hb->dbuf.chksum));
+
+  if (fchk != chk)
+    {
+      DEBUG(0, ("checksums don't match %d %d\n", fchk, chk));
+      return -1;
+    }
+
+  strcpy(finfo->name, prefix);
+
+  /* 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);
+
+/* can't handle links at present */
+  if (hb->dbuf.linkflag != '0') {
+    if (hb->dbuf.linkflag == 0) {
+      DEBUG(6, ("Warning: NULL link flag (gnu tar archive ?) %s\n",
+               finfo->name));
+    } else { 
+      DEBUG(0, ("this tar file appears to contain some kind of link - ignoring\n"));
+      return -2;
+    }
+  }
+    
+  if ((unoct(hb->dbuf.mode, sizeof(hb->dbuf.mode)) & S_IFDIR)
+    || (*(finfo->name+strlen(finfo->name)-1) == '\\'))
+    {
+      finfo->mode=aDIR;
+    }
+  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;
+
+  /* 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 a 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 (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()
+{
+  /* initialize tar buffer */
+  tbufsiz=blocksize*TBLOCK;
+  tarbuf=malloc(tbufsiz);
+
+  /* reset tar buffer pointer and tar file counter */
+  tp=0; ntarf=0;
+}
+
+/****************************************************************************
+Write two zero blocks at end of file
+****************************************************************************/
+static void dotareof(int f)
+{
+  struct stat stbuf;
+  /* Two zero blocks at end of file, write out full buffer */
+
+  (void) dozerobuf(f, TBLOCK);
+  (void) dozerobuf(f, TBLOCK);
+
+  if (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, char *fp, int l)
+{
+  /* add a '.' to start of file name, convert from ugly dos \'s in path
+   * to lovely unix /'s :-} */
+
+  *tptr++='.';
+#ifdef KANJI
+  while (l > 0) {
+    if (is_shift_jis (*fp)) {
+      *tptr++ = *fp++;
+      *tptr++ = *fp++;
+      l -= 2;
+    } else if (is_kana (*fp)) {
+      *tptr++ = *fp++;
+      l--;
+    } else if (*fp == '\\') {
+      *tptr++ = '/';
+      fp++;
+      l--;
+    } else {
+      *tptr++ = *fp++;
+      l--;
+    }
+  }
+#else
+  while (l--) { *tptr=(*fp == '\\') ? '/' : *fp; tptr++; fp++; }
+#endif
+}
+
+/****************************************************************************
+Convert from decimal to octal string
+****************************************************************************/
+static void oct_it (register long value, register int ndgs, register 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(*p))
+        value = (value << 3) | (long) (*p - '0');
+
+      p++;
+    }
+
+  return value;
+}
+
+/****************************************************************************
+Compare two strings in a slash insensitive way
+***************************************************************************/
+int strslashcmp(const char *s1, const char *s2)
+{
+  while(*s1 && *s2 &&
+       (*s1 == *s2
+        || tolower(*s1) == tolower(*s2)
+        || (*s1 == '\\' && *s2=='/')
+        || (*s1 == '/' && *s2=='\\'))) {
+         s1++; s2++;
+  }
+
+  return *s1-*s2;
+}
+
+/*
+ * general smb utility functions
+ */
+/****************************************************************************
+Set DOS file attributes
+***************************************************************************/
+static int do_setrattr(char *fname, int attr, int setit)
+{
+  /*
+   * First get the existing attribs from existing file
+   */
+  char *inbuf,*outbuf;
+  char *p;
+  pstring name;
+  int fattr;
+
+  strcpy(name,fname);
+  strcpy(fname,"\\");
+  strcat(fname,name);
+
+  inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+  outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+
+  if (!inbuf || !outbuf)
+    {
+      DEBUG(0,("out of memory\n"));
+      return False;
+    }
+
+  /* send an smb getatr message */
+
+  memset(outbuf,0,smb_size);
+  set_message(outbuf,0,2 + strlen(fname),True);
+  CVAL(outbuf,smb_com) = SMBgetatr;
+  SSVAL(outbuf,smb_tid,cnum);
+  setup_pkt(outbuf);
+
+  p = smb_buf(outbuf);
+  *p++ = 4;
+  strcpy(p,fname);
+  p += (strlen(fname)+1);
+  
+  *p++ = 4;
+  *p++ = 0;
+
+  send_smb(Client,outbuf);
+  receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+  if (CVAL(inbuf,smb_rcls) != 0)
+    DEBUG(5,("getatr: %s\n",smb_errstr(inbuf)));
+  else
+    {
+      DEBUG(5,("\nattr 0x%X  time %d  size %d\n",
+              (int)CVAL(inbuf,smb_vwv0),
+              SVAL(inbuf,smb_vwv1),
+              SVAL(inbuf,smb_vwv3)));
+    }
+
+  fattr=CVAL(inbuf,smb_vwv0);
+
+  /* combine found attributes with bits to be set or reset */
+
+  attr=setit ? (fattr | attr) : (fattr & ~attr);
+
+  /* now try and set attributes by sending smb reset message */
+
+  /* clear out buffer and start again */
+  memset(outbuf,0,smb_size);
+  set_message(outbuf,8,4 + strlen(fname),True);
+  CVAL(outbuf,smb_com) = SMBsetatr;
+  SSVAL(outbuf,smb_tid,cnum);
+  setup_pkt(outbuf);
+
+  SSVAL(outbuf,smb_vwv0,attr);
+
+  p = smb_buf(outbuf);
+  *p++ = 4;      
+  strcpy(p,fname);
+  p += (strlen(fname)+1);
+  
+  *p++ = 4;
+  *p++ = 0;
+
+  send_smb(Client,outbuf);
+  receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+  
+  if (CVAL(inbuf,smb_rcls) != 0)
+    {
+      DEBUG(0,("%s setting attributes on file %s\n",
+           smb_errstr(inbuf), fname));
+      free(inbuf);free(outbuf);
+      return(False);
+    }
+
+  free(inbuf);free(outbuf);
+  return(True);
+}
+
+/****************************************************************************
+Create a file on a share
+***************************************************************************/
+static BOOL smbcreat(file_info finfo, int *fnum, char *inbuf, char *outbuf)
+{
+  char *p;
+  /* *must* be called with buffer ready malloc'ed */
+  /* open remote file */
+  
+  memset(outbuf,0,smb_size);
+  set_message(outbuf,3,2 + strlen(finfo.name),True);
+  CVAL(outbuf,smb_com) = SMBcreate;
+  SSVAL(outbuf,smb_tid,cnum);
+  setup_pkt(outbuf);
+  
+  SSVAL(outbuf,smb_vwv0,finfo.mode);
+  put_dos_date3(outbuf,smb_vwv1,finfo.mtime);
+  
+  p = smb_buf(outbuf);
+  *p++ = 4;      
+  strcpy(p,finfo.name);
+  
+  send_smb(Client,outbuf);
+  receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+  
+  if (CVAL(inbuf,smb_rcls) != 0)
+    {
+      DEBUG(0,("%s opening remote file %s\n",smb_errstr(inbuf),
+              finfo.name));
+      return 0;
+    }
+  
+  *fnum = SVAL(inbuf,smb_vwv0);
+  return True;
+}
+
+/****************************************************************************
+Write a file to a share
+***************************************************************************/
+static BOOL smbwrite(int fnum, int n, int low, int high, int left,
+                    char *bufferp, char *inbuf, char *outbuf)
+{
+  /* *must* be called with buffer ready malloc'ed */
+
+  memset(outbuf,0,smb_size);
+  set_message(outbuf,5,n + 3,True);
+  
+  memcpy(smb_buf(outbuf)+3, bufferp, n);
+  
+  set_message(outbuf,5,n + 3, False);
+  CVAL(outbuf,smb_com) = SMBwrite;
+  SSVAL(outbuf,smb_tid,cnum);
+  setup_pkt(outbuf);
+  
+  SSVAL(outbuf,smb_vwv0,fnum);
+  SSVAL(outbuf,smb_vwv1,n);
+  SIVAL(outbuf,smb_vwv2,low);
+  SSVAL(outbuf,smb_vwv4,left);
+  CVAL(smb_buf(outbuf),0) = 1;
+  SSVAL(smb_buf(outbuf),1,n);
+
+  send_smb(Client,outbuf); 
+  receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+  
+  if (CVAL(inbuf,smb_rcls) != 0)
+    {
+      DEBUG(0,("%s writing remote file\n",smb_errstr(inbuf)));
+      return False;
+    }
+  
+  if (n != SVAL(inbuf,smb_vwv0))
+    {
+      DEBUG(0,("Error: only wrote %d bytes out of %d\n",
+              SVAL(inbuf,smb_vwv0), n));
+      return False;
+    }
+
+  return True;
+}
+
+/****************************************************************************
+Close a file on a share
+***************************************************************************/
+static BOOL smbshut(file_info finfo, int fnum, char *inbuf, char *outbuf)
+{
+  /* *must* be called with buffer ready malloc'ed */
+
+  memset(outbuf,0,smb_size);
+  set_message(outbuf,3,0,True);
+  CVAL(outbuf,smb_com) = SMBclose;
+  SSVAL(outbuf,smb_tid,cnum);
+  setup_pkt(outbuf);
+  
+  SSVAL(outbuf,smb_vwv0,fnum);
+  put_dos_date3(outbuf,smb_vwv1,finfo.mtime);
+  
+  DEBUG(3,("Setting date to %s (0x%X)",
+          asctime(LocalTime(&finfo.mtime,GMT_TO_LOCAL)),
+          finfo.mtime));
+  
+  send_smb(Client,outbuf);
+  receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+  
+  if (CVAL(inbuf,smb_rcls) != 0)
+    {
+      DEBUG(0,("%s closing remote file %s\n",smb_errstr(inbuf),
+              finfo.name));
+      return False;
+    }
+
+  return True;
+}
+
+/****************************************************************************
+Verify existence of path on share
+***************************************************************************/
+static BOOL smbchkpath(char *fname, char *inbuf, char *outbuf)
+{
+  char *p;
+
+  memset(outbuf,0,smb_size);
+  set_message(outbuf,0,4 + strlen(fname),True);
+  CVAL(outbuf,smb_com) = SMBchkpth;
+  SSVAL(outbuf,smb_tid,cnum);
+  setup_pkt(outbuf);
+
+  p = smb_buf(outbuf);
+  *p++ = 4;
+  strcpy(p,fname);
+
+  send_smb(Client,outbuf);
+  receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+  DEBUG(5,("smbchkpath: %s\n",smb_errstr(inbuf)));
+
+  return(CVAL(inbuf,smb_rcls) == 0);
+}
+
+/****************************************************************************
+Make a directory on share
+***************************************************************************/
+static BOOL smbmkdir(char *fname, char *inbuf, char *outbuf)
+{
+  /* *must* be called with buffer ready malloc'ed */
+  char *p;
+
+  memset(outbuf,0,smb_size);
+  set_message(outbuf,0,2 + strlen(fname),True);
+  
+  CVAL(outbuf,smb_com) = SMBmkdir;
+  SSVAL(outbuf,smb_tid,cnum);
+  setup_pkt(outbuf);
+  
+  p = smb_buf(outbuf);
+  *p++ = 4;      
+  strcpy(p,fname);
+  
+  send_smb(Client,outbuf);
+  receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+  
+  if (CVAL(inbuf,smb_rcls) != 0)
+    {
+      DEBUG(0,("%s making remote directory %s\n",
+              smb_errstr(inbuf),fname));
+      return(False);
+    }
+
+  return(True);
+}
+
+/****************************************************************************
+Ensure a remote path exists (make if necessary)
+***************************************************************************/
+static BOOL ensurepath(char *fname, char *inbuf, char *outbuf)
+{
+  /* *must* be called with buffer ready malloc'ed */
+  /* ensures path exists */
+
+  pstring partpath, ffname;
+  char *p=fname, *basehack;
+
+  *partpath = 0;
+
+  /* fname copied to ffname so can strtok */
+
+  strcpy(ffname, fname);
+
+  /* do a `basename' on ffname, so don't try and make file name directory */
+  if ((basehack=strrchr(ffname, '\\')) == NULL)
+    return True;
+  else
+    *basehack='\0';
+
+  p=strtok(ffname, "\\");
+
+  while (p)
+    {
+      strcat(partpath, p);
+
+      if (!smbchkpath(partpath, inbuf, outbuf)) {
+       if (!smbmkdir(partpath, inbuf, outbuf))
+         {
+           DEBUG(0, ("Error mkdirhiering\n"));
+           return False;
+         }
+       else
+         DEBUG(3, ("mkdirhiering %s\n", partpath));
+
+      }
+
+      strcat(partpath, "\\");
+      p = strtok(NULL,"/\\");
+    }
+
+    return True;
+}
+
+/*
+ * smbclient functions
+ */
+/****************************************************************************
+append one remote file to the tar file
+***************************************************************************/
+static void do_atar(char *rname,char *lname,file_info *finfo1)
+{
+  int fnum;
+  uint32 nread=0;
+  char *p;
+  char *inbuf,*outbuf;
+  file_info finfo;
+  BOOL close_done = False;
+  BOOL shallitime=True;
+  BOOL ignore_close_error = False;
+  char *dataptr=NULL;
+  int datalen=0;
+
+  struct timeval tp_start;
+  GetTimeOfDay(&tp_start);
+
+  if (finfo1) 
+    finfo = *finfo1;
+  else
+    finfo = def_finfo;
+
+  inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+  outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+
+  if (!inbuf || !outbuf)
+    {
+      DEBUG(0,("out of memory\n"));
+      return;
+    }
+
+  memset(outbuf,0,smb_size);
+  set_message(outbuf,15,1 + strlen(rname),True);
+
+  CVAL(outbuf,smb_com) = SMBopenX;
+  SSVAL(outbuf,smb_tid,cnum);
+  setup_pkt(outbuf);
+
+  SSVAL(outbuf,smb_vwv0,0xFF);
+  SSVAL(outbuf,smb_vwv2,1);
+  SSVAL(outbuf,smb_vwv3,(DENY_NONE<<4));
+  SSVAL(outbuf,smb_vwv4,aSYSTEM | aHIDDEN);
+  SSVAL(outbuf,smb_vwv5,aSYSTEM | aHIDDEN);
+  SSVAL(outbuf,smb_vwv8,1);
+
+  p = smb_buf(outbuf);
+  strcpy(p,rname);
+  p = skip_string(p,1);
+
+  dos_clean_name(rname);
+
+  /* do a chained openX with a readX? */  
+  if (finfo.size > 0)
+    {
+      SSVAL(outbuf,smb_vwv0,SMBreadX);
+      SSVAL(outbuf,smb_vwv1,PTR_DIFF(p,outbuf) - 4);
+      memset(p,0,200);
+      p -= smb_wct;
+      SSVAL(p,smb_wct,10);
+      SSVAL(p,smb_vwv0,0xFF);
+      SSVAL(p,smb_vwv5,MIN(max_xmit-500,finfo.size));
+      SSVAL(p,smb_vwv9,MIN(0xFFFF,finfo.size));
+      smb_setlen(outbuf,smb_len(outbuf)+11*2+1);  
+    }
+  
+  send_smb(Client,outbuf);
+  receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+  if (CVAL(inbuf,smb_rcls) != 0)
+    {
+      if (CVAL(inbuf,smb_rcls) == ERRSRV &&
+         SVAL(inbuf,smb_err) == ERRnoresource &&
+         reopen_connection(inbuf,outbuf))
+       {
+         do_atar(rname,lname,finfo1);
+         free(inbuf);free(outbuf);
+         return;
+       }
+
+      DEBUG(0,("%s opening remote file %s\n",smb_errstr(inbuf),rname));
+      free(inbuf);free(outbuf);
+      return;
+    }
+
+  strcpy(finfo.name,rname);
+  if (!finfo1)
+    {
+      finfo.mode = SVAL(inbuf,smb_vwv3);
+      finfo.size = IVAL(inbuf,smb_vwv4);
+      finfo.mtime = make_unix_date3(inbuf+smb_vwv6);
+      finfo.atime = finfo.ctime = finfo.mtime;
+    }
+
+  DEBUG(3,("file %s attrib 0x%X\n",finfo.name,finfo.mode));
+
+  fnum = SVAL(inbuf,smb_vwv2);
+
+  if (tar_inc && !(finfo.mode & aARCH))
+    {
+      DEBUG(4, ("skipping %s - archive bit not set\n", finfo.name));
+      shallitime=0;
+    }
+  else
+    {
+      if (SVAL(inbuf,smb_vwv0) == SMBreadX)
+       {
+         p = (inbuf+4+SVAL(inbuf,smb_vwv1)) - smb_wct;
+         datalen = SVAL(p,smb_vwv5);
+         dataptr = inbuf + 4 + SVAL(p,smb_vwv6);
+       }
+      else
+       {
+         dataptr = NULL;
+         datalen = 0;
+       }
+
+      DEBUG(2,("getting file %s of size %d bytes as a tar file %s",
+              finfo.name,
+              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");
+      
+      while (nread < finfo.size && !close_done)
+       {
+         int method = -1;
+         static BOOL can_chain_close=True;
+
+         p=NULL;
+         
+         DEBUG(3,("nread=%d\n",nread));
+         
+         /* 3 possible read types. readbraw if a large block is required.
+            readX + close if not much left and read if neither is supported */
+
+         /* we might have already read some data from a chained readX */
+         if (dataptr && datalen>0)
+           method=3;
+         
+         /* if we can finish now then readX+close */
+         if (method<0 && can_chain_close && (Protocol >= PROTOCOL_LANMAN1) && 
+             ((finfo.size - nread) < 
+              (max_xmit - (2*smb_size + 13*SIZEOFWORD + 300))))
+           method = 0;
+         
+         /* if we support readraw then use that */
+         if (method<0 && readbraw_supported)
+           method = 1;
+         
+         /* if we can then use readX */
+         if (method<0 && (Protocol >= PROTOCOL_LANMAN1))
+           method = 2;
+         
+         
+         switch (method)
+           {
+             /* use readX */
+           case 0:
+           case 2:
+             if (method == 0)
+               close_done = True;
+             
+             /* use readX + close */
+             memset(outbuf,0,smb_size);
+             set_message(outbuf,10,0,True);
+             CVAL(outbuf,smb_com) = SMBreadX;
+             SSVAL(outbuf,smb_tid,cnum);
+             setup_pkt(outbuf);
+             
+             if (close_done)
+               {
+                 CVAL(outbuf,smb_vwv0) = SMBclose;
+                 SSVAL(outbuf,smb_vwv1,PTR_DIFF(smb_buf(outbuf),outbuf) - 4);
+               }
+             else
+               CVAL(outbuf,smb_vwv0) = 0xFF;         
+             
+             
+             SSVAL(outbuf,smb_vwv2,fnum);
+             SIVAL(outbuf,smb_vwv3,nread);
+             SSVAL(outbuf,smb_vwv5,MIN(max_xmit-200,finfo.size - nread));
+             SSVAL(outbuf,smb_vwv6,0);
+             SIVAL(outbuf,smb_vwv7,0);
+             SSVAL(outbuf,smb_vwv9,MIN(0xFFFF,finfo.size-nread));
+             
+             if (close_done)
+               {
+                 p = smb_buf(outbuf);
+                 memset(p,0,9);
+                 
+                 CVAL(p,0) = 3;
+                 SSVAL(p,1,fnum);
+                 SIVALS(p,3,-1);
+                 
+                 /* now set the total packet length */
+                 smb_setlen(outbuf,smb_len(outbuf)+9);
+               }
+             
+             send_smb(Client,outbuf);
+             receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+             
+             if (CVAL(inbuf,smb_rcls) != 0)
+               {
+                 DEBUG(0,("Error %s reading remote file\n",smb_errstr(inbuf)));
+                 break;
+               }
+             
+             if (close_done &&
+                 SVAL(inbuf,smb_vwv0) != SMBclose)
+               {
+                 /* NOTE: WfWg sometimes just ignores the chained
+                    command! This seems to break the spec? */
+                 DEBUG(3,("Rejected chained close?\n"));
+                 close_done = False;
+                 can_chain_close = False;
+                 ignore_close_error = True;
+               }
+             
+             datalen = SVAL(inbuf,smb_vwv5);
+             dataptr = inbuf + 4 + SVAL(inbuf,smb_vwv6);
+             break;
+             
+             
+             /* use readbraw */
+           case 1:
+             {
+               static int readbraw_size = 0xFFFF;
+               
+               extern int Client;
+               memset(outbuf,0,smb_size);
+               set_message(outbuf,8,0,True);
+               CVAL(outbuf,smb_com) = SMBreadbraw;
+               SSVAL(outbuf,smb_tid,cnum);
+               setup_pkt(outbuf);
+               SSVAL(outbuf,smb_vwv0,fnum);
+               SIVAL(outbuf,smb_vwv1,nread);
+               SSVAL(outbuf,smb_vwv3,MIN(finfo.size-nread,readbraw_size));
+               SSVAL(outbuf,smb_vwv4,0);
+               SIVALS(outbuf,smb_vwv5,-1);
+               send_smb(Client,outbuf);
+               
+               /* Now read the raw data into the buffer and write it */          
+               if(read_smb_length(Client,inbuf,0) == -1) {
+                 DEBUG(0,("Failed to read length in readbraw\n"));         
+                 exit(1);
+               }
+               
+               /* Even though this is not an smb message, smb_len
+                  returns the generic length of an smb message */
+               datalen = smb_len(inbuf);
+               
+               if (datalen == 0)
+                 {
+                   /* we got a readbraw error */
+                   DEBUG(4,("readbraw error - reducing size\n"));
+                   readbraw_size = (readbraw_size * 9) / 10;
+                   
+                   if (readbraw_size < max_xmit)
+                     {
+                       DEBUG(0,("disabling readbraw\n"));
+                       readbraw_supported = False;
+                     }
+
+                   dataptr=NULL;
+                   continue;
+                 }
+
+               if(read_data(Client,inbuf,datalen) != datalen) {
+                 DEBUG(0,("Failed to read data in readbraw\n"));
+                 exit(1);
+               }
+               dataptr = inbuf;
+             }
+             break;
+
+           case 3:
+             /* we've already read some data with a chained readX */
+             break;
+             
+           default:
+             /* use plain read */
+             memset(outbuf,0,smb_size);
+             set_message(outbuf,5,0,True);
+             CVAL(outbuf,smb_com) = SMBread;
+             SSVAL(outbuf,smb_tid,cnum);
+             setup_pkt(outbuf);
+             
+             SSVAL(outbuf,smb_vwv0,fnum);
+             SSVAL(outbuf,smb_vwv1,MIN(max_xmit-200,finfo.size - nread));
+             SIVAL(outbuf,smb_vwv2,nread);
+             SSVAL(outbuf,smb_vwv4,finfo.size - nread);
+             
+             send_smb(Client,outbuf);
+             receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+             
+             if (CVAL(inbuf,smb_rcls) != 0)
+               {
+                 DEBUG(0,("Error %s reading remote file\n",smb_errstr(inbuf)));
+                 break;
+               }
+             
+             datalen = SVAL(inbuf,smb_vwv0);
+             dataptr = smb_buf(inbuf) + 3;
+             break;
+           }
+         
+         
+         /* add received bits of file to buffer - dotarbuf will
+          * write out in 512 byte intervals */
+         if (dotarbuf(tarhandle,dataptr,datalen) != datalen)
+           {
+             DEBUG(0,("Error writing local file\n"));
+             break;
+           }
+         
+         nread += datalen;
+         if (datalen == 0) 
+           {
+             DEBUG(0,("Error reading file %s. Got 0 bytes\n", rname));
+             break;
+           }
+
+         dataptr=NULL;
+         datalen=0;
+       }
+      
+      /* round tar file to nearest block */
+      if (finfo.size % TBLOCK)
+       dozerobuf(tarhandle, TBLOCK - (finfo.size % TBLOCK));
+      
+      ntarf++;
+    }
+  
+  if (!close_done)
+    {
+      memset(outbuf,0,smb_size);
+      set_message(outbuf,3,0,True);
+      CVAL(outbuf,smb_com) = SMBclose;
+      SSVAL(outbuf,smb_tid,cnum);
+      setup_pkt(outbuf);
+      
+      SSVAL(outbuf,smb_vwv0,fnum);
+      SIVALS(outbuf,smb_vwv1,-1);
+      
+      send_smb(Client,outbuf);
+      receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+      
+      if (!ignore_close_error && CVAL(inbuf,smb_rcls) != 0)
+       {
+         DEBUG(0,("Error %s closing remote file\n",smb_errstr(inbuf)));
+         free(inbuf);free(outbuf);
+         return;
+       }
+    }
+
+  if (shallitime)
+    {
+      struct timeval tp_end;
+      int this_time;
+
+      /* if shallitime is true then we didn't skip */
+      if (tar_reset) (void) do_setrattr(finfo.name, aARCH, 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;
+
+      /* Thanks to Carel-Jan Engel (ease@mail.wirehub.nl) for this one */
+      DEBUG(2,("(%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))));
+    }
+  
+  free(inbuf);free(outbuf);
+}
+
+/****************************************************************************
+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;
+
+    strcpy(exclaim, cur_dir);
+    *(exclaim+strlen(exclaim)-1)='\0';
+
+    if (clipfind(cliplist, clipn, exclaim)) {
+      DEBUG(3,("Skipping directory %s\n", exclaim));
+      return;
+    }
+
+    strcat(exclaim, "\\");
+    strcat(exclaim, finfo->name);
+
+    if (clipfind(cliplist, clipn, exclaim)) {
+      DEBUG(3,("Skipping file %s\n", exclaim));
+      return;
+    }
+  }
+
+  if (finfo->mode & aDIR)
+    {
+      pstring saved_curdir;
+      pstring mtar_mask;
+      char *inbuf,*outbuf;
+
+      inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+      outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+
+      if (!inbuf || !outbuf)
+       {
+         DEBUG(0,("out of memory\n"));
+         return;
+       }
+
+      strcpy(saved_curdir,cur_dir);
+
+      strcat(cur_dir,finfo->name);
+      strcat(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");
+      strcpy(mtar_mask,cur_dir);
+      strcat(mtar_mask,"*");
+      
+      do_dir((char *)inbuf,(char *)outbuf,mtar_mask,attribute,do_tar,recurse);
+      strcpy(cur_dir,saved_curdir);
+      free(inbuf);free(outbuf);
+    }
+  else
+    {
+      strcpy(rname,cur_dir);
+      strcat(rname,finfo->name);
+      do_atar(rname,finfo->name,finfo);
+    }
+}
+
+/****************************************************************************
+Convert from UNIX to DOS file names
+***************************************************************************/
+static void unfixtarname(char *tptr, char *fp, int l)
+{
+  /* remove '.' from start of file name, convert from unix /'s to
+   * dos \'s in path. Kill any absolute path names.
+   */
+
+  if (*fp == '.') fp++;
+  if (*fp == '\\' || *fp == '/') fp++;
+
+#ifdef KANJI
+  while (l > 0) {
+    if (is_shift_jis (*fp)) {
+      *tptr++ = *fp++;
+      *tptr++ = *fp++;
+      l -= 2;
+    } else if (is_kana (*fp)) {
+      *tptr++ = *fp++;
+      l--;
+    } else if (*fp == '/') {
+      *tptr++ = '\\';
+      fp++;
+      l--;
+    } else {
+      *tptr++ = *fp++;
+      l--;
+    }
+  }
+#else
+  while (l--) { *tptr=(*fp == '/') ? '\\' : *fp; tptr++; fp++; }
+#endif
+}
+
+static void do_tarput()
+{
+  file_info finfo;
+  int nread=0, bufread;
+  char *inbuf,*outbuf; 
+  int fsize=0;
+  int fnum;
+  struct timeval tp_start;
+  BOOL tskip=False;       /* We'll take each file as it comes */
+
+  GetTimeOfDay(&tp_start);
+  
+  inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+  outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+  
+  if (!inbuf || !outbuf)
+    {
+      DEBUG(0,("out of memory\n"));
+      return;
+    }
+  
+  /*
+   * Must read in tbufsiz dollops
+   */
+
+  /* These should be the only reads in clitar.c */
+  while ((bufread=read(tarhandle, tarbuf, tbufsiz))>0) {
+    char *bufferp, *endofbuffer;
+    int chunk;
+
+    /* Code to handle a short read.
+     * We always need a TBLOCK full of stuff
+     */
+    if (bufread % TBLOCK) {
+      int lchunk=TBLOCK-(bufread % TBLOCK);
+      int lread;
+
+      /* It's a shorty - a short read that is */
+      DEBUG(3, ("Short read, read %d so far (need %d)\n", bufread, lchunk));
+
+      while ((lread=read(tarhandle, tarbuf+bufread, lchunk))>0) {
+       bufread+=lread;
+       if (!(lchunk-=lread)) break;
+      }
+
+      /* If we've reached EOF then that must be a short file */
+      if (lread<=0) break;
+    }
+
+    bufferp=tarbuf; 
+    endofbuffer=tarbuf+bufread;
+
+    if (tskip) {
+      if (fsize<bufread) {
+       tskip=False;
+       bufferp+=fsize;
+       fsize=0;
+      } else {
+       if (fsize==bufread) tskip=False;
+       fsize-=bufread;
+       continue;
+      }
+    }
+
+    do {
+      if (!fsize)
+       {
+         switch (readtarheader((union hblock *) bufferp, &finfo, cur_dir))
+           {
+           case -2:             /* something dodgy but not fatal about this */
+             DEBUG(0, ("skipping %s...\n", finfo.name));
+             bufferp+=TBLOCK;   /* header - like a link */
+             continue;
+           case -1:
+             DEBUG(0, ("abandoning restore\n"));
+             free(inbuf); free(outbuf);
+             return;
+           case 0: /* chksum is zero - we assume that one all zero
+                    *header block will do for eof */
+             DEBUG(0,
+                   ("total of %d tar files restored to share\n", ntarf));
+             free(inbuf); free(outbuf);
+             return;
+           default:
+             break;
+           }
+
+         tskip=clipn
+           && (clipfind(cliplist, clipn, finfo.name) ^ tar_excl);
+         if (tskip) {
+           bufferp+=TBLOCK;
+           if (finfo.mode & aDIR)
+             continue;
+           else if ((fsize=finfo.size) % TBLOCK) {
+             fsize+=TBLOCK-(fsize%TBLOCK);
+           }
+           if (fsize<endofbuffer-bufferp) {
+             bufferp+=fsize;
+             fsize=0;
+             continue;
+           } else {
+             fsize-=endofbuffer-bufferp;
+             break;
+           }
+         }
+
+         if (finfo.mode & aDIR)
+           {
+             if (!smbchkpath(finfo.name, inbuf, outbuf)
+                 && !smbmkdir(finfo.name, inbuf, outbuf))
+               {
+                 DEBUG(0, ("abandoning restore\n"));
+                 free(inbuf); free(outbuf);
+                 return;
+             }
+             else
+               {
+                 bufferp+=TBLOCK;
+                 continue;
+               }
+           }
+         
+         fsize=finfo.size;
+
+         if (ensurepath(finfo.name, inbuf, outbuf)
+             && !smbcreat(finfo, &fnum, inbuf, outbuf))
+           {
+             DEBUG(0, ("abandoning restore\n"));
+             free(inbuf);free(outbuf);
+             return;
+           }
+
+         DEBUG(0,("restore tar file %s of size %d bytes\n",
+                  finfo.name,finfo.size));
+
+         nread=0;
+         if ((bufferp+=TBLOCK) >= endofbuffer) break;    
+       } /* if (!fsize) */
+       
+      /* write out the file in chunk sized chunks - don't
+       * go past end of buffer though */
+      chunk=(fsize-nread < endofbuffer - bufferp)
+       ? fsize - nread : endofbuffer - bufferp;
+      
+      while (chunk > 0) {
+       int minichunk=MIN(chunk, max_xmit-200);
+       
+       if (!smbwrite(fnum, /* file descriptor */
+                     minichunk, /* n */
+                     nread, /* offset low */
+                     0, /* offset high - not implemented */
+                     fsize-nread, /* left - only hint to server */
+                     bufferp,
+                     inbuf,
+                     outbuf))
+         {
+           DEBUG(0, ("Error writing remote file\n"));
+           free(inbuf); free(outbuf);
+           return;
+         }
+       DEBUG(5, ("chunk writing fname=%s fnum=%d nread=%d minichunk=%d chunk=%d size=%d\n", finfo.name, fnum, nread, minichunk, chunk, fsize));
+       
+       bufferp+=minichunk; nread+=minichunk;
+       chunk-=minichunk;
+      }
+      
+      if (nread>=fsize)
+       {
+         if (!smbshut(finfo, fnum, inbuf, outbuf))
+           {
+             DEBUG(0, ("Error closing remote file\n"));
+             free(inbuf);free(outbuf);
+             return;
+           }
+         if (fsize % TBLOCK) bufferp+=TBLOCK - (fsize % TBLOCK);
+         DEBUG(5, ("bufferp is now %d (psn=%d)\n",
+                   (long) bufferp, (long)(bufferp - tarbuf)));
+         ntarf++;
+         fsize=0;
+       }
+    } while (bufferp < endofbuffer);
+  }
+
+  DEBUG(0, ("premature eof on tar file ?\n"));
+  DEBUG(0,("total of %d tar files restored to share\n", ntarf));
+
+  free(inbuf); free(outbuf);
+}
+
+/*
+ * samba interactive commands
+ */
+
+/****************************************************************************
+Blocksize command
+***************************************************************************/
+void cmd_block(void)
+{
+  fstring buf;
+  int block;
+
+  if (!next_token(NULL,buf,NULL))
+    {
+      DEBUG(0, ("blocksize <n>\n"));
+      return;
+    }
+
+  block=atoi(buf);
+  if (block < 0 || block > 65535)
+    {
+      DEBUG(0, ("blocksize out of range"));
+      return;
+    }
+
+  blocksize=block;
+  DEBUG(2,("blocksize is now %d\n", blocksize));
+}
+
+/****************************************************************************
+command to set incremental / reset mode
+***************************************************************************/
+void cmd_tarmode(void)
+{
+  fstring buf;
+
+  while (next_token(NULL,buf,NULL)) {
+    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 DEBUG(0, ("tarmode: unrecognised option %s\n", buf));
+  }
+
+  DEBUG(0, ("tarmode is now %s, %s\n",
+           tar_inc ? "incremental" : "full",
+           tar_reset ? "reset" : "noreset"));
+}
+
+/****************************************************************************
+Feeble attrib command
+***************************************************************************/
+void cmd_setmode(void)
+{
+  char *q;
+  fstring buf;
+  pstring fname;
+  int attra[2];
+  int direct=1;
+
+  attra[0] = attra[1] = 0;
+
+  if (!next_token(NULL,buf,NULL))
+    {
+      DEBUG(0, ("setmode <filename> <perm=[+|-]rsha>\n"));
+      return;
+    }
+
+  strcpy(fname, cur_dir);
+  strcat(fname, buf);
+
+  while (next_token(NULL,buf,NULL)) {
+    q=buf;
+
+    while(*q)
+      switch (*q++) {
+      case '+': direct=1;
+       break;
+      case '-': direct=0;
+       break;
+      case 'r': attra[direct]|=aRONLY;
+       break;
+      case 'h': attra[direct]|=aHIDDEN;
+       break;
+      case 's': attra[direct]|=aSYSTEM;
+       break;
+      case 'a': attra[direct]|=aARCH;
+       break;
+      default: DEBUG(0, ("setmode <filename> <perm=[+|-]rsha>\n"));
+       return;
+      }
+  }
+
+  if (attra[ATTRSET]==0 && attra[ATTRRESET]==0)
+    {
+      DEBUG(0, ("setmode <filename> <perm=[+|-]rsha>\n"));
+      return;
+    }
+
+DEBUG(2, ("\nperm set %d %d\n", attra[ATTRSET], attra[ATTRRESET]));
+  (void) do_setrattr(fname, attra[ATTRSET], ATTRSET);
+  (void) do_setrattr(fname, attra[ATTRRESET], ATTRRESET);
+}
+
+/****************************************************************************
+Principal command for creating / extracting
+***************************************************************************/
+void cmd_tar(char *inbuf, char *outbuf)
+{
+  fstring buf;
+  char **argl;
+  int argcl;
+
+  if (!next_token(NULL,buf,NULL))
+    {
+      DEBUG(0,("tar <c|x>[IXbga] <filename>\n"));
+      return;
+    }
+
+  argl=toktocliplist(&argcl, NULL);
+  if (!tar_parseargs(argcl, argl, buf, 0))
+    return;
+
+  process_tar(inbuf, outbuf);
+
+  free(argl);
+}
+
+/****************************************************************************
+Command line (option) version
+***************************************************************************/
+int process_tar(char *inbuf, char *outbuf)
+{
+  initarbuf();
+  switch(tar_type) {
+  case 'x':
+    do_tarput();
+    free(tarbuf);
+    close(tarhandle);
+    break;
+  case 'r':
+  case 'c':
+    if (clipn && tar_excl) {
+      int i;
+      pstring tarmac;
+
+      for (i=0; i<clipn; i++) {
+       DEBUG(0,("arg %d = %s\n", i, cliplist[i]));
+
+       if (*(cliplist[i]+strlen(cliplist[i])-1)=='\\') {
+         *(cliplist[i]+strlen(cliplist[i])-1)='\0';
+       }
+       
+       if (strrchr(cliplist[i], '\\')) {
+         pstring saved_dir;
+         
+         strcpy(saved_dir, cur_dir);
+         
+         if (*cliplist[i]=='\\') {
+           strcpy(tarmac, cliplist[i]);
+         } else {
+           strcpy(tarmac, cur_dir);
+           strcat(tarmac, cliplist[i]);
+         }
+         strcpy(cur_dir, tarmac);
+         *(strrchr(cur_dir, '\\')+1)='\0';
+
+         do_dir((char *)inbuf,(char *)outbuf,tarmac,attribute,do_tar,recurse);
+         strcpy(cur_dir,saved_dir);
+       } else {
+         strcpy(tarmac, cur_dir);
+         strcat(tarmac, cliplist[i]);
+         do_dir((char *)inbuf,(char *)outbuf,tarmac,attribute,do_tar,recurse);
+       }
+      }
+    } else {
+      pstring mask;
+      strcpy(mask,cur_dir);
+      strcat(mask,"\\*");
+      do_dir((char *)inbuf,(char *)outbuf,mask,attribute,do_tar,recurse);
+    }
+    
+    if (ntarf) dotareof(tarhandle);
+    close(tarhandle);
+    free(tarbuf);
+    
+    DEBUG(0, ("tar: dumped %d tar files\n", ntarf));
+    break;
+  }
+
+  return(0);
+}
+
+/****************************************************************************
+Find a token (filename) in a clip list
+***************************************************************************/
+int clipfind(char **aret, int ret, char *tok)
+{
+  if (aret==NULL) return 0;
+
+  /* ignore leading slashes or dots in token */
+  while(strchr("/\\.", *tok)) tok++;
+
+  while(ret--) {
+    char *pkey=*aret++;
+
+    /* ignore leading slashes or dots in list */
+    while(strchr("/\\.", *pkey)) pkey++;
+
+    if (!strslashcmp(pkey, tok)) return 1;
+  }
+
+  return 0;
+}
+
+/****************************************************************************
+Parse tar arguments. Sets tar_type, tar_excl, etc.
+***************************************************************************/
+int tar_parseargs(int argc, char *argv[], 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;
+
+  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 {
+       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,GMT_TO_LOCAL))));
+         Optind++;
+       } else {
+         DEBUG(0,("Error setting newer-than time\n"));
+         return 0;
+       }
+      }
+      break;
+    case 'a':
+      tar_reset=True;
+      break;
+    case 'I':
+      if (tar_clipfl) {
+       DEBUG(0,("Only one of I,X must be specified\n"));
+       return 0;
+      }
+      tar_clipfl='I';
+      break;
+    case 'X':
+      if (tar_clipfl) {
+       DEBUG(0,("Only one of I,X must be specified\n"));
+       return 0;
+      }
+      tar_clipfl='X';
+      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;
+  }
+
+  if (Optind>=argc || !strcmp(argv[Optind], "-")) {
+    /* Sets tar handle to either 0 or 1, as appropriate */
+    tarhandle=(tar_type=='c');
+  } else {
+    tar_excl=tar_clipfl!='X';
+    
+    if (Optind+1<argc) {
+      cliplist=argv+Optind+1;
+      clipn=argc-Optind-1;
+    }
+
+    if ((tar_type=='x' && (tarhandle = open(argv[Optind], O_RDONLY)) == -1)
+       || (tar_type=='c' && (tarhandle=creat(argv[Optind], 0644)) < 0))
+      {
+       DEBUG(0,("Error opening local file %s\n",argv[Optind]));
+       return(0);
+      }
+  }
+
+  return 1;
+}
diff --git a/source/include/byteorder.h b/source/include/byteorder.h
new file mode 100644 (file)
index 0000000..899cd6c
--- /dev/null
@@ -0,0 +1,80 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   SMB Byte handling
+   Copyright (C) Andrew Tridgell 1992-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.
+*/
+
+/*
+   This file implements macros for machine independent short and 
+   int manipulation
+*/
+
+#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 char *)(buf))[pos])
+#define PVAL(buf,pos) ((unsigned)CVAL(buf,pos))
+#define SCVAL(buf,pos,val) (CVAL(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(buf,pos)=(val)&0xFF,CVAL(buf,pos+1)=(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
+/* 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 
+*/
+#define SVAL(buf,pos) (*(uint16 *)((char *)(buf) + (pos)))
+#define IVAL(buf,pos) (*(uint32 *)((char *)(buf) + (pos)))
+#define SVALS(buf,pos) (*(int16 *)((char *)(buf) + (pos)))
+#define IVALS(buf,pos) (*(int32 *)((char *)(buf) + (pos)))
+#define SSVAL(buf,pos,val) SVAL(buf,pos)=((uint16)(val))
+#define SIVAL(buf,pos,val) IVAL(buf,pos)=((uint32)(val))
+#define SSVALS(buf,pos,val) SVALS(buf,pos)=((int16)(val))
+#define SIVALS(buf,pos,val) IVALS(buf,pos)=((int32)(val))
+#endif
+
+
+/* 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 RIVAL(buf,pos) IREV(IVAL(buf,pos))
+#define RSSVAL(buf,pos,val) SSVAL(buf,pos,SREV(val))
+#define RSIVAL(buf,pos,val) SIVAL(buf,pos,IREV(val))
diff --git a/source/include/charset.h b/source/include/charset.h
new file mode 100644 (file)
index 0000000..7091732
--- /dev/null
@@ -0,0 +1,61 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   Character set handling
+   Copyright (C) Andrew Tridgell 1992-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.
+*/
+
+#ifndef CHARSET_C
+
+extern char *dos_char_map;
+extern char *upper_char_map;
+extern char *lower_char_map;
+extern void add_char_string(char *s);
+extern void charset_initialise(void);
+
+#ifdef toupper
+#undef toupper
+#endif
+
+#ifdef tolower
+#undef tolower
+#endif
+
+#ifdef isupper
+#undef isupper
+#endif
+
+#ifdef islower
+#undef islower
+#endif
+
+#ifdef isdoschar
+#undef isdoschar
+#endif
+
+#ifdef isspace
+#undef isspace
+#endif
+
+#define toupper(c) upper_char_map[(char)(c)]
+#define tolower(c) lower_char_map[(char)(c)]
+#define isupper(c) (((char)(c)) != tolower(c))
+#define islower(c) (((char)(c)) != toupper(c))
+#define isdoschar(c) (dos_char_map[(char)(c)] != 0)
+#define isspace(c) ((c)==' ' || (c) == '\t')
+#endif
+
diff --git a/source/include/clitar.h b/source/include/clitar.h
new file mode 100644 (file)
index 0000000..2305fce
--- /dev/null
@@ -0,0 +1,17 @@
+
+#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;
+};
diff --git a/source/include/includes.h b/source/include/includes.h
new file mode 100644 (file)
index 0000000..cc2bbbf
--- /dev/null
@@ -0,0 +1,1154 @@
+#ifndef _INCLUDES_H
+#define _INCLUDES_H
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   Machine customisation and include handling
+   Copyright (C) Andrew Tridgell 1994-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.
+*/
+/*
+   This file does all the #includes's. This makes it easier to
+   port to a new unix. Hopefully a port will only have to edit the Makefile
+   and add a section for the new unix below.
+*/
+
+
+/* the first OS dependent section is to setup what includes will be used.
+   the main OS dependent section comes later on 
+*/
+
+#ifdef ALTOS
+#define NO_UTIMEH
+#endif
+
+#ifdef MIPS
+#define POSIX_H
+#define NO_UTIMEH
+#endif
+
+#ifdef sun386
+#define NO_UTIMEH
+#endif
+
+#ifdef NEXT2
+#define NO_UTIMEH
+#endif
+
+#ifdef NEXT3_0
+#define NO_UTIMEH
+#define NO_UNISTDH
+#endif
+
+#ifdef APOLLO
+#define NO_UTIMEH
+#define NO_SYSMOUNTH
+#define NO_UNISTDH
+#endif
+
+#ifdef AIX
+#define NO_SYSMOUNTH
+#endif
+
+#ifdef M88K_R3
+#define SVR3H
+#define NO_RESOURCEH
+#endif
+
+#ifdef DNIX
+#define NO_SYSMOUNTH
+#define NO_NETIFH
+#define NO_RESOURCEH
+#define PRIME_NMBD 0
+#define NO_SETGROUPS
+#endif
+
+
+#ifdef ISC
+#define SYSSTREAMH
+#define NO_RESOURCEH
+#endif
+
+#ifdef QNX
+#define NO_RESOURCEH
+#define NO_SYSMOUNTH
+#define USE_MMAP 1
+#ifdef __386__
+   #define __i386__
+#endif
+#endif
+
+#ifdef NEWS42
+#define NO_UTIMEH
+#define NO_STRFTIME
+#define NO_UTIMBUF
+#define REPLACE_MKTIME
+#define NO_TM_NAME
+#endif
+
+#ifdef OS2
+#define NO_SYSMOUNTH
+#define NO_NETIFH
+#endif
+
+#ifdef LYNX
+#define NO_SYSMOUNTH
+#endif
+
+
+#if (defined(SHADOW_PWD)||defined(OSF1_ENH_SEC)||defined(SecureWare)||defined(PWDAUTH))
+#define PASSWORD_LENGTH 16
+#endif
+
+/* here is the general includes section - with some ifdefs generated 
+   by the previous section 
+*/
+#include "local.h"
+#include <stdio.h>
+#ifdef POSIX_STDLIBH
+#include <posix/stdlib.h>
+#else
+#include <stdlib.h>
+#endif
+#include <ctype.h>
+#include <time.h>
+#ifndef NO_UTIMEH
+#include <utime.h>
+#endif
+#include <sys/types.h>
+
+#ifdef SVR3H
+#include <sys/statfs.h>
+#include <sys/stream.h>
+#include <netinet/types.h>
+#include <netinet/ether.h>
+#include <netinet/ip_if.h>
+#endif
+
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <stddef.h>
+#ifdef POSIX_H
+#include <posix/utime.h>
+#include <bsd/sys/time.h>
+#include <bsd/netinet/in.h>
+#else
+#include <sys/time.h>
+#include <netinet/in.h>
+#endif 
+#include <netdb.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <grp.h>
+#ifndef NO_RESOURCEH
+#include <sys/resource.h>
+#endif
+#ifndef NO_SYSMOUNTH
+#include <sys/mount.h>
+#endif
+#include <pwd.h>
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#ifndef NO_UNISTDH
+#include <unistd.h>
+#endif
+#include <sys/wait.h>
+#ifdef SYSSTREAMH
+#include <sys/stream.h>
+#endif
+#ifndef NO_NETIFH
+#ifdef POSIX_H
+#include <bsd/net/if.h>
+#else
+#include <net/if.h>
+#endif
+#endif
+
+#if USE_MMAP
+#include <sys/mman.h>
+#endif
+
+#if defined(GETPWANAM)
+#include <sys/types.h>
+#include <sys/label.h>
+#include <sys/audit.h>
+#include <pwdadj.h>
+#endif
+
+#if defined(SHADOW_PWD) && !defined(NETBSD) && !defined(CONVEX)
+#include <shadow.h>
+#endif
+
+/* this might be different on different systems */
+#ifdef QUOTAS 
+#ifdef LINUX
+#ifdef __KERNEL__
+#undef __KERNEL__
+#include <sys/quota.h>
+#define __KERNEL__
+#else
+#include <sys/quota.h>
+#endif
+#include <mntent.h>
+#else
+#include <sys/quota.h>
+#ifndef CRAY
+#include <devnm.h>
+#else
+#include <mntent.h>
+#endif
+#endif
+#endif
+
+#ifdef SYSLOG
+#include <syslog.h>
+#endif
+
+
+
+/***************************************************************************
+Here come some platform specific sections
+***************************************************************************/
+
+
+#ifdef LINUX
+#include <arpa/inet.h>
+#include <dirent.h>
+#include <string.h>
+#include <sys/vfs.h>
+#include <netinet/in.h>
+#ifndef NO_ASMSIGNALH
+#include <asm/signal.h>
+#endif
+#define SIGNAL_CAST (__sighandler_t)
+#define USE_GETCWD
+#define USE_SETSID
+#define HAVE_BZERO
+#define HAVE_MEMMOVE
+#ifdef SHADOW_PWD
+#ifndef crypt
+#define crypt pw_encrypt
+#endif
+#endif
+#endif
+
+#ifdef SUNOS4
+#define SIGNAL_CAST (void (*)(int))
+#include <netinet/tcp.h>
+#include <dirent.h>
+#include <sys/acct.h>
+#include <sys/vfs.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <signal.h>
+/* #include <termios.h> */
+#ifdef sun386
+#define NO_STRFTIME
+#define NO_UTIMBUF
+#define mktime timelocal
+typedef unsigned short mode_t;
+#else
+#include <utime.h>
+#define NO_STRERROR
+#endif
+#define REPLACE_GETPASS
+#define BSD_TERMIO
+#endif
+
+
+#ifdef SUNOS5
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/acct.h>
+#include <sys/statfs.h>
+#include <sys/statvfs.h>
+#include <sys/filio.h>
+#include <sys/sockio.h>
+#include <netinet/in_systm.h>
+#include <netinet/tcp.h>
+#include <netinet/ip.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <rpcsvc/ypclnt.h>
+#include <crypt.h> 
+#include <termios.h>
+extern int gettimeofday (struct timeval *, void *);
+extern int gethostname (char *name, int namelen);
+extern int innetgr (const char *, const char *, const char *, const char *);
+#define USE_SETVBUF
+#define SIGNAL_CAST (void (*)(int))
+#ifndef SYSV
+#define SYSV
+#endif
+#define USE_WAITPID
+#define REPLACE_STRLEN
+#define USE_STATVFS
+#define USE_GETCWD
+#define USE_SETSID
+#define REPLACE_GETPASS
+#endif
+
+
+#ifdef ULTRIX
+#include <strings.h>
+#include <nfs/nfs_clnt.h>
+#include <nfs/vfs.h>
+#include <netinet/tcp.h>
+#ifdef ULTRIX_AUTH
+#include <auth.h>
+#endif
+char *getwd(char *);
+#define NOSTRDUP
+#ifdef __STDC__
+#define SIGNAL_CAST (void(*)(int))
+#endif
+#define USE_DIRECT
+#endif
+
+#ifdef SGI
+#include <netinet/tcp.h>
+#include <sys/statfs.h>
+#include <string.h>
+#include <signal.h>
+#ifndef SYSV
+#define SYSV
+#endif
+#define SIGNAL_CAST (void (*)())
+#define STATFS4
+#define USE_WAITPID
+#define USE_DIRECT
+#endif
+
+#ifdef SGI5
+#include <netinet/tcp.h>
+#include <sys/statvfs.h>
+#include <string.h>
+#include <signal.h>
+#include <dirent.h>
+#define USE_WAITPID
+#define NETGROUP 
+#ifndef SYSV
+#define SYSV
+#endif
+#define SIGNAL_CAST (void (*)())
+#define USE_STATVFS
+#define USE_WAITPID
+#endif
+
+
+#ifdef MIPS
+#include <bsd/net/soioctl.h>
+#include <string.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/statfs.h>
+#include <sys/wait.h>
+#include <sys/termio.h>
+#define SIGNAL_CAST (void (*)())
+typedef int mode_t;
+extern struct group *getgrnam();
+extern struct passwd *getpwnam();
+#define STATFS4
+#define NO_STRERROR
+#define REPLACE_STRSTR
+#endif /* MIPS */
+
+
+
+#ifdef DGUX
+#include <string.h>
+#include <dirent.h>
+#include <sys/statfs.h>
+#include <sys/statvfs.h>
+#include <fcntl.h>
+#include <termios.h>
+#define SYSV
+#define USE_WAITPID
+#define SIGNAL_CAST (void (*)(int))
+#define STATFS4
+#define USE_GETCWD
+#endif
+
+
+#ifdef SVR4
+#include <string.h>
+#include <sys/dir.h>
+#include <dirent.h>
+#include <sys/statfs.h>
+#include <sys/statvfs.h>
+#include <sys/vfs.h>
+#include <sys/filio.h>
+#include <fcntl.h>
+#include <sys/sockio.h>
+#include <netinet/tcp.h>
+#include <stropts.h>
+#include <termios.h>
+#define SYSV
+#define USE_WAITPID
+#define SIGNAL_CAST (void (*)(int))
+#define USE_STATVFS
+#define USE_GETCWD
+#define USE_SETSID
+#endif
+
+
+#ifdef OSF1
+#include <termios.h>
+#include <strings.h>
+#include <dirent.h>
+char *getwd(char *);
+char *mktemp(char *); /* No standard include */
+#include <netinet/in.h>
+#include <arpa/inet.h> /* both for inet_ntoa */
+#define SIGNAL_CAST ( void (*) (int) )
+#define STATFS3
+#define USE_F_FSIZE
+#include <netinet/tcp.h>
+#ifdef OSF1_ENH_SEC
+#include <pwd.h>
+#include <sys/types.h>
+#include <sys/security.h>
+#include <prot.h>
+#include <unistd.h>
+#define PASSWORD_LENGTH 16
+#define NEED_AUTH_PARAMETERS
+#endif  /* OSF1_ENH_SEC */
+#endif
+
+
+#ifdef CLIX
+#include <dirent.h>
+#define SIGNAL_CAST    (void (*)())
+#include <sys/fcntl.h>
+#include <sys/statfs.h>
+#include <string.h>
+#define NO_EID
+#define USE_WAITPID
+#define STATFS4
+#define NO_FSYNC
+#define USE_GETCWD
+#define USE_SETSID
+#define REPLACE_GETPASS
+#define NO_GETRLIMIT
+#endif /* CLIX */
+
+
+
+#ifdef BSDI
+#include <string.h>
+#include <netinet/tcp.h>
+#define SIGNAL_CAST (void (*)())
+#define USE_DIRECT
+#endif
+
+
+#ifdef NETBSD
+#include <strings.h>
+#include <netinet/tcp.h>
+/* you may not need this */
+#define NO_GETSPNAM
+#define SIGNAL_CAST (void (*)())
+#define USE_DIRECT
+#define REPLACE_INNETGR
+#endif 
+
+
+
+#ifdef FreeBSD
+#include <strings.h>
+#include <netinet/tcp.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#define SIGNAL_CAST (void (*)())
+#define USE_DIRECT
+#define REPLACE_INNETGR
+#endif 
+
+
+
+#ifdef AIX
+#include <strings.h>
+#include <sys/dir.h>
+#include <sys/select.h>
+#include <dirent.h>
+#include <sys/statfs.h>
+#include <sys/vfs.h>
+#include <sys/id.h>
+#include <sys/priv.h>
+#include <netinet/tcp.h>
+#define SYSV
+#define USE_WAITPID
+#define SIGNAL_CAST (void (*)())
+#define DEFAULT_PRINTING PRINT_AIX
+#endif
+
+
+#ifdef HPUX
+#include <string.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/vfs.h>
+#include <sys/types.h>
+#include <sys/termios.h>
+#include <netinet/tcp.h>
+#ifdef HPUX_10_TRUSTED
+#include <hpsecurity.h>
+#include <prot.h>
+#define NEED_AUTH_PARAMETERS
+#endif
+#define SIGNAL_CAST (void (*)(__harg))
+#define SELECT_CAST (int *)
+#define SYSV
+#define USE_WAITPID
+#define WAIT3_CAST2 (int *)
+#define USE_GETCWD
+#define USE_SETSID
+#define USE_SETRES
+#define DEFAULT_PRINTING PRINT_HPUX
+#define SIGCLD_IGNORE
+#endif
+
+
+#ifdef SEQUENT
+#include <signal.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/statfs.h>
+#include <sys/stat.h>
+#include <sys/buf.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <fcntl.h>
+#define SIGNAL_CAST (void (*)(int))
+#define USE_WAITPID
+#define USE_GETCWD
+#define NO_EID
+#define STATFS4
+#define USE_DIRECT
+#endif
+
+#ifdef NEXT2
+#include <sys/types.h>
+#include <strings.h>
+#include <dirent.h>
+#include <sys/vfs.h>
+#define bzero(b,len) memset(b,0,len)
+#define mode_t int
+#define NO_UTIMBUF
+#include <libc.h>
+#define NOSTRDUP
+#define USE_DIRECT
+#define USE_WAITPID
+#endif 
+
+
+#ifdef NEXT3_0
+#include <strings.h>
+#include <sys/dir.h>
+#include <sys/vfs.h>
+#define bzero(b,len) memset(b,0,len)
+#define NO_UTIMBUF
+#include <libc.h>
+#define NOSTRDUP
+#define USE_DIRECT
+#define mode_t int
+#define GID_TYPE int
+#define gid_t int
+#define SIGNAL_CAST (void (*)(int))
+#define WAIT3_CAST1 (union wait *)
+#define HAVE_GMTOFF
+#endif
+
+
+
+#ifdef APOLLO
+#include <string.h>
+#include <fcntl.h>
+#include <sys/statfs.h>
+#define NO_UTIMBUF
+#define USE_DIRECT
+#define USE_GETCWD
+#define SIGNAL_CAST     (void (*)())
+#define HAVE_FCNTL_LOCK 0
+#define HAVE_GETTIMEOFDAY
+#define STATFS4
+#endif
+
+
+
+#ifdef SCO
+#include <sys/netinet/tcp.h>
+#include <sys/netinet/in_systm.h>
+#include <sys/netinet/ip.h>
+#include <dirent.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/statfs.h>
+#include <sys/stropts.h>
+#include <limits.h>
+#ifdef EVEREST
+#include <unistd.h> 
+#endif
+#ifdef NETGROUP
+#include <rpcsvc/ypclnt.h>
+#endif
+#ifdef SecureWare
+#include <sys/security.h>
+#include <sys/audit.h>
+#include <prot.h>
+#define crypt bigcrypt
+#endif
+#ifndef EVEREST
+ #define ftruncate(f,l) syscall(0x0a28,f,l)
+#endif 
+#define SIGNAL_CAST (void (*)(int))
+#define USE_WAITPID
+#define USE_GETCWD
+#define USE_SETSID
+#ifdef SCO3_2_2
+#define NO_EID
+#else
+#ifndef EVEREST
+#define USE_IFREQ
+#endif
+#endif
+#define STATFS4
+#define NO_FSYNC
+#ifndef EVEREST
+#define NO_INITGROUPS
+#endif
+#define HAVE_PATHCONF
+#define NO_GETRLIMIT
+#endif
+
+
+
+/* Definitions for RiscIX */
+#ifdef RiscIX
+#define SIGNAL_CAST (void (*)(int))
+#include <sys/dirent.h>
+#include <sys/acct.h>
+#include <sys/vfs.h>
+#include <string.h>
+#include <utime.h>
+#include <signal.h>
+#define HAVE_GETTIMEOFDAY
+#define NOSTRCASECMP
+#define NOSTRDUP
+#endif
+
+
+
+#ifdef ISC
+#include <net/errno.h>
+#include <string.h>
+#include <sys/dir.h>
+#include <dirent.h>
+#include <sys/statfs.h>
+#include <fcntl.h>
+#include <sys/sioctl.h>
+#include <stropts.h>
+#include <limits.h>
+#include <netinet/tcp.h>
+#define FIONREAD FIORDCHK
+#define SYSV
+#define USE_WAITPID
+#define SIGNAL_CAST (void (*)(int))
+#define USE_GETCWD
+#define USE_SETSID
+#define USE_IFREQ
+#define NO_FTRUNCATE
+#define STATFS4
+#define NO_FSYNC
+#endif
+
+
+
+#ifdef AUX
+#include <fstab.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/vfs.h>
+#include <fcntl.h>
+#include <termios.h>
+#define SYSV
+#define USE_WAITPID
+#define SIGNAL_CAST (void (*)(int))
+char *strdup (char *);
+#define USE_GETCWD
+#endif
+
+
+#ifdef M88K_R3
+#include <string.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <termios.h>
+#define STATFS4
+#define SYSV
+#define USE_WAITPID
+#define SIGNAL_CAST (void (*)(int))
+char *strdup (char *);
+#define USE_GETCWD
+#define NO_FSYNC
+#define NO_EID
+#endif
+
+
+#ifdef DNIX
+#include <dirent.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/statfs.h>
+#include <sys/stropts.h>
+#define NO_GET_BROADCAST
+#define USE_WAITPID
+#define USE_GETCWD
+#define USE_SETSID
+#define STATFS4
+#define NO_EID
+#define PF_INET AF_INET
+#define NO_STRERROR
+#define ftruncate(f,l) chsize(f,l)
+#endif /* DNIX */
+
+#ifdef CONVEX
+#define SIGNAL_CAST (void (*)(int))
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <dirent.h>
+#include <string.h>
+#include <sys/vfs.h>
+#include <fcntl.h>
+#define DONT_REINSTALL_SIG
+#define USE_SIGBLOCK
+#define USE_WAITPID
+#define SIGNAL_CAST (_SigFunc_Ptr_t)
+#define NO_GETSPNAM
+#define HAVE_MEMMOVE
+extern char *mktemp(char *);
+extern int  fsync(int);
+extern int  seteuid(uid_t);
+extern int  setgroups(int, int *);
+extern int  initgroups(char *, int);
+extern int  statfs(char *, struct statfs *);
+extern int  setegid(gid_t);
+extern int  getopt(int, char *const *, const char *);
+extern int  chroot(char *);
+extern int  gettimeofday(struct timeval *, struct timezone *);
+extern int  gethostname(char *, int);
+extern char *crypt(char *, char *);
+extern char *getpass(char *);
+#endif
+
+
+#ifdef CRAY
+#define MAXPATHLEN 1024
+#include <dirent.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/statfs.h>
+#define SIGNAL_CAST (void (*)(int))
+#define SIGCLD_IGNORE
+#define HAVE_FCNTL_LOCK 1
+#define USE_SETSID
+#define STATFS4
+#endif
+
+
+#ifdef ALTOS
+#include <unistd.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/fcntl.h>
+#include <sys/statfs.h>
+#define        const
+#define        uid_t           int
+#define        gid_t           int
+#define        mode_t          int
+#define        ptrdiff_t       int
+#define HAVE_GETGRNAM  0
+#define NO_EID
+#define NO_FSYNC
+#define        NO_FTRUNCATE
+#define        NO_GETRLIMIT
+#define        NO_INITGROUPS
+#define NO_SELECT
+#define NO_SETGROUPS
+#define NO_STRERROR
+#define NO_STRFTIME
+#define        NO_TM_NAME
+#define NO_UTIMEH
+#define NOSTRCASECMP
+#define REPLACE_MKTIME
+#define REPLACE_RENAME
+#define REPLACE_STRSTR
+#define STATFS4
+#define        USE_GETCWD
+#endif
+
+#ifdef QNX
+#define STATFS4
+#include <sys/statfs.h>
+#include <sys/select.h>
+#include <signal.h>
+#include <sys/dir.h>
+#define SIGNAL_CAST (void (*)())
+#define USE_WAITPID
+#define NO_INITGROUPS
+#define NO_SETGROUPS
+#define HAVE_TIMEZONE
+#define USE_GETCWD
+#define USE_SETSID
+#define HAVE_FCNTL_LOCK 1
+#define DEFAULT_PRINTING PRINT_QNX
+#endif
+
+
+#ifdef NEWS42
+#include <string.h>
+#include <dirent.h>
+#include <sys/vfs.h>
+#include <sys/timeb.h>
+typedef int mode_t;
+#endif
+
+#ifdef OS2
+#include <dirent.h>
+#include <sys/statfs.h>
+#include <string.h>
+#include <limits.h>
+#define SIGNAL_CAST (void (*)())
+#define HAVE_FCNTL_LOCK 0
+#define USE_WAITPID
+#define NO_GET_BROADCAST
+#define NO_EID
+#define NO_SETGROUPS
+#define NO_INITGROUPS
+#define NO_CRYPT
+#define NO_STATFS
+#define NO_CHROOT
+#define NO_CHOWN
+#define strcasecmp stricmp
+#define strncasecmp strnicmp
+#endif
+
+
+#ifdef LYNX
+#define SIGNAL_CAST (void (*)())
+#define WAIT3_CAST1 (union wait *)
+#define STATFS4
+#include <fcntl.h>
+#include <resource.h>
+#include <stat.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/statfs.h>
+#define USE_GETCWD
+#define USE_GETSID
+#endif
+
+
+/*******************************************************************
+end of the platform specific sections
+********************************************************************/
+
+#ifdef SecureWare
+#define NEED_AUTH_PARAMETERS
+#endif
+
+#ifdef REPLACE_GETPASS
+extern char    *getsmbpass(char *);
+#define getpass(s) getsmbpass(s)
+#endif
+
+#ifdef REPLACE_INNETGR
+#define innetgr(group,host,user,dom) InNetGr(group,host,user,dom)
+#endif
+
+#ifndef FD_SETSIZE
+#define FD_SETSIZE 255
+#endif
+
+#ifndef MAXINT
+#define MAXINT ((((unsigned)1)<<(sizeof(int)*8-1))-1)
+#endif
+
+#ifndef __STDC__
+#define const
+#endif
+
+/* Now for some other grungy stuff */
+#ifdef NO_GETSPNAM
+struct spwd { /* fake shadow password structure */
+       char *sp_pwdp;
+};
+#endif
+
+#ifndef HAVE_BZERO
+#ifndef bzero
+#define bzero(p,s) memset(p,0,s)
+#endif
+#endif
+
+#ifndef HAVE_MEMMOVE
+#ifndef memmove
+#define memmove(d,s,n) MemMove(d,s,n)
+#endif
+#endif
+
+#ifdef USE_DIRECT
+#include <sys/dir.h>
+#endif
+
+/* some unixes have ENOTTY instead of TIOCNOTTY */
+#ifndef TIOCNOTTY
+#ifdef ENOTTY
+#define TIOCNOTTY ENOTTY
+#endif
+#endif
+
+#ifndef SIGHUP
+#define SIGHUP 1
+#endif
+
+/* if undefined then use bsd or sysv printing */
+#ifndef DEFAULT_PRINTING
+#ifdef SYSV
+#define DEFAULT_PRINTING PRINT_SYSV
+#else
+#define DEFAULT_PRINTING PRINT_BSD
+#endif
+#endif
+
+
+#ifdef AFS_AUTH
+#include <afs/stds.h>
+#include <afs/kautils.h>
+#endif
+
+#ifdef DFS_AUTH
+#include <dce/dce_error.h>
+#include <dce/sec_login.h>
+#endif
+
+#ifdef NO_UTIMBUF
+struct utimbuf {
+  time_t actime;
+  time_t modtime;
+};
+#endif
+
+#ifdef NO_STRERROR
+#ifndef strerror
+extern char *sys_errlist[];
+#define strerror(i) sys_errlist[i]
+#endif
+#endif
+
+#ifndef perror
+#define perror(m) printf("%s: %s\n",m,strerror(errno))
+#endif
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 255
+#endif
+
+#include "version.h"
+#include "smb.h"
+#include "byteorder.h"
+#ifdef SMB_PASSWD
+#include "smbpass.h"
+#endif
+
+#include "kanji.h"
+#include "charset.h"
+
+#ifndef S_IFREG
+#define S_IFREG 0100000
+#endif
+
+#ifndef S_ISREG
+#define S_ISREG(x) ((S_IFREG & x)!=0)
+#endif
+
+#ifndef S_ISDIR
+#define S_ISDIR(x) ((S_IFDIR & x)!=0)
+#endif
+
+#ifdef UFC_CRYPT
+#define crypt ufc_crypt
+#endif
+
+#ifdef REPLACE_STRLEN
+#define strlen(s) Strlen(s)
+#endif
+
+#ifdef REPLACE_STRSTR
+#define strstr(s,p) Strstr(s,p)
+#endif
+
+#ifdef REPLACE_MKTIME
+#define mktime(t) Mktime(t)
+#endif
+
+#ifndef NGROUPS_MAX
+#define NGROUPS_MAX 128
+#endif
+
+#ifndef EDQUOT
+#define EDQUOT ENOSPC
+#endif
+
+#ifndef HAVE_GETGRNAM
+#define HAVE_GETGRNAM 1
+#endif
+
+#ifndef SOL_TCP
+#define SOL_TCP 6
+#endif
+
+/* default to using ftruncate workaround as this is safer than assuming
+it works and getting lots of bug reports */
+#ifndef FTRUNCATE_CAN_EXTEND
+#define FTRUNCATE_CAN_EXTEND 0
+#endif
+
+/* maybe this unix doesn't separate RD and WR locks? */
+#ifndef F_RDLCK
+#define F_RDLCK F_WRLCK
+#endif
+
+#ifndef ENOTSOCK
+#define ENOTSOCK EINVAL
+#endif
+
+#ifndef SIGCLD
+#define SIGCLD SIGCHLD
+#endif 
+
+#ifndef HAVE_FCNTL_LOCK
+#define HAVE_FCNTL_LOCK 1
+#endif
+
+#ifndef WAIT3_CAST2
+#define WAIT3_CAST2 (struct rusage *)
+#endif
+
+#ifndef WAIT3_CAST1
+#define WAIT3_CAST1 (int *)
+#endif
+
+#ifndef QSORT_CAST
+#define QSORT_CAST (int (*)())
+#endif
+
+/* this is a rough check to see if this machine has a lstat() call.
+   it is not guaranteed to work */
+#if !(defined(S_ISLNK) || defined(S_IFLNK))
+#define lstat stat
+#endif
+
+/* Not all systems declare ERRNO in errno.h... and some systems #define it! */
+#ifndef errno
+extern int errno;
+#endif 
+
+
+#ifdef NO_EID
+#define geteuid() getuid()
+#define getegid() getgid()
+#define seteuid(x) setuid(x)
+#define setegid(x) setgid(x)
+#endif
+
+
+#if (HAVE_FCNTL_LOCK == 0)
+/* since there is no locking available, system includes  */
+/* for DomainOS 10.4 do not contain any of the following */
+/* #define's. So, to satisfy the compiler, add these     */
+/* #define's, although they arn't really necessary.      */
+#define F_GETLK 0
+#define F_SETLK 0
+#define F_WRLCK 0
+#define F_UNLCK 0
+#endif /* HAVE_FCNTL_LOCK == 0 */
+
+#ifdef NOSTRCASECMP
+#define strcasecmp(s1,s2) StrCaseCmp(s1,s2)
+#define strncasecmp(s1,s2,n) StrnCaseCmp(s1,s2,n)
+#endif
+
+#ifndef strcpy
+#define strcpy(dest,src) StrCpy(dest,src)
+#endif
+
+
+/* possibly wrap the malloc calls */
+#if WRAP_MALLOC
+
+/* undo the old malloc def if necessary */
+#ifdef malloc
+#define xx_old_malloc malloc
+#undef malloc
+#endif
+
+#define malloc(size) malloc_wrapped(size,__FILE__,__LINE__)
+
+/* undo the old realloc def if necessary */
+#ifdef realloc
+#define xx_old_realloc realloc
+#undef realloc
+#endif
+
+#define realloc(ptr,size) realloc_wrapped(ptr,size,__FILE__,__LINE__)
+
+/* undo the old free def if necessary */
+#ifdef free
+#define xx_old_free free
+#undef free
+#endif
+
+#define free(ptr) free_wrapped(ptr,__FILE__,__LINE__)
+
+/* and the malloc prototypes */
+void *malloc_wrapped(int,char *,int);
+void *realloc_wrapped(void *,int,char *,int);
+void free_wrapped(void *,char *,int);
+
+#endif
+
+
+#if WRAP_MEMCPY
+/* undo the old memcpy def if necessary */
+#ifdef memcpy
+#define xx_old_memcpy memcpy
+#undef memcpy
+#endif
+
+#define memcpy(d,s,l) memcpy_wrapped(d,s,l,__FILE__,__LINE__)
+void *memcpy_wrapped(void *d,void *s,int l,char *fname,int line);
+#endif
+
+#endif
diff --git a/source/include/kanji.h b/source/include/kanji.h
new file mode 100644 (file)
index 0000000..4f18305
--- /dev/null
@@ -0,0 +1,130 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   Kanji Extensions
+   Copyright (C) Andrew Tridgell 1992-1994
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+   Adding for Japanese language by <fujita@ainix.isac.co.jp> 1994.9.5
+     and extend coding system to EUC/SJIS/JIS/HEX at 1994.10.11
+     and add all jis codes sequence at 1995.8.16
+     Notes: Hexadecimal code by <ohki@gssm.otuka.tsukuba.ac.jp>
+*/
+#ifndef _KANJI_H_
+#define _KANJI_H_
+
+#ifdef KANJI
+
+/* FOR SHIFT JIS CODE */
+#define is_shift_jis(c) \
+    ((0x81 <= ((unsigned char) (c)) && ((unsigned char) (c)) <= 0x9f) \
+     || (0xe0 <= ((unsigned char) (c)) && ((unsigned char) (c)) <= 0xef))
+#define is_shift_jis2(c) \
+    (0x40 <= ((unsigned char) (c)) && ((unsigned char) (c)) <= 0xfc \
+    && ((unsigned char) (c)) != 0x7f)
+#define is_kana(c) ((0xa0 <= ((unsigned char) (c)) && ((unsigned char) (c)) <= 0xdf))
+
+#ifdef _KANJI_C_
+/* FOR EUC CODE */
+#define euc_kana (0x8e)
+#define is_euc_kana(c) (((unsigned char) (c)) == euc_kana)
+#define is_euc(c)  (0xa0 < ((unsigned char) (c)) && ((unsigned char) (c)) < 0xff)
+
+/* FOR JIS CODE */
+/* default jis third shift code, use for output */
+#ifndef JIS_KSO
+#define JIS_KSO 'B'
+#endif
+#ifndef JIS_KSI
+#define JIS_KSI 'J'
+#endif
+/* in: \E$B or \E$@ */
+/* out: \E(J or \E(B or \E(H */
+#define jis_esc (0x1b)
+#define jis_so (0x0e)
+#define jis_so1 ('$')
+#define jis_so2 ('B')
+#define jis_si (0x0f)
+#define jis_si1 ('(')
+#define jis_si2 ('J')
+#define is_esc(c) (((unsigned char) (c)) == jis_esc)
+#define is_so1(c) (((unsigned char) (c)) == jis_so1)
+#define is_so2(c) (((unsigned char) (c)) == jis_so2 || ((unsigned char) (c)) == '@')
+#define is_si1(c) (((unsigned char) (c)) == jis_si1)
+#define is_si2(c) (((unsigned char) (c)) == jis_si2 || ((unsigned char) (c)) == 'B' \
+    || ((unsigned char) (c)) == 'H')
+#define is_so(c) (((unsigned char) (c)) == jis_so)
+#define is_si(c) (((unsigned char) (c)) == jis_si)
+#define junet_kana1 ('(')
+#define junet_kana2 ('I')
+#define is_juk1(c) (((unsigned char) (c)) == junet_kana1)
+#define is_juk2(c) (((unsigned char) (c)) == junet_kana2)
+
+#define _KJ_ROMAN (0)
+#define _KJ_KANJI (1)
+#define _KJ_KANA (2)
+
+/* FOR HEX */
+#define HEXTAG ':'
+#define hex2bin(x)                                                   \
+    ( ((int) '0' <= ((int) (x)) && ((int) (x)) <= (int)'9')?         \
+        (((int) (x))-(int)'0'):                                              \
+      ((int) 'a'<= ((int) (x)) && ((int) (x))<= (int) 'f')?          \
+        (((int) (x)) - (int)'a'+10):                                 \
+      (((int) (x)) - (int)'A'+10) )
+#define bin2hex(x)                                                   \
+    ( (((int) (x)) >= 10)? (((int) (x))-10 + (int) 'a'): (((int) (x)) + (int) '0') )
+
+#else /* not _KANJI_C_ */
+
+extern char* (*_dos_to_unix) (const char *str, BOOL overwrite);
+extern char* (*_unix_to_dos) (const char *str, BOOL overwrite);
+
+#define unix_to_dos (*_unix_to_dos)
+#define dos_to_unix (*_dos_to_unix)
+
+extern char *sj_strtok (char *s1, const char *s2);
+extern char *sj_strchr (const char *s, int c);
+extern char *sj_strrchr (const char *s, int c);
+extern char *sj_strstr (const char *s1, const char *s2);
+
+#define strchr sj_strchr
+#define strrchr sj_strrchr
+#define strstr sj_strstr
+#define strtok sj_strtok
+
+#endif /* _KANJI_C_ */
+
+#define UNKNOWN_CODE (-1)
+#define SJIS_CODE (0)
+#define EUC_CODE (1)
+#define JIS7_CODE (2)
+#define JIS8_CODE (3)
+#define JUNET_CODE (4)
+#define HEX_CODE (5)
+#define CAP_CODE (6)
+#define DOSV_CODE SJIS_CODE
+
+int interpret_coding_system (char *str, int def);
+
+#else 
+
+#define unix_to_dos(x,y) (x)
+#define dos_to_unix(x,y) (x)
+
+#endif /* not KANJI */
+
+#endif /* _KANJI_H_ */
diff --git a/source/include/local.h b/source/include/local.h
new file mode 100644 (file)
index 0000000..2775453
--- /dev/null
@@ -0,0 +1,167 @@
+/* local definitions for file server */
+#ifndef _LOCAL_H
+#define _LOCAL_H
+
+/* 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"
+
+/* This defines the name of the printcap file. It is MOST UNLIKELY that
+   this will change BUT! Specifying a file with the format of a printcap
+   file but containing only a subset of the printers actually in your real 
+   printcap file is a quick-n-dirty way to allow dynamic access to a subset
+   of available printers.
+*/
+#define PRINTCAP_NAME "/etc/printcap"
+
+/* 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 127
+#define MAX_OPEN_FILES 100
+
+/* the max number of connections that the smbstatus program will show */
+#define MAXSTATUS 1000
+
+/* 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 MAXDIR 64
+
+#define WORDMAX 0xFFFF
+
+
+/* separators for lists */
+#define LIST_SEP " \t,;:\n\r"
+
+#ifndef LOCKDIR
+#define LOCKDIR "/tmp/samba"
+#endif
+
+/* this is where browse lists are kept in the lock dir */
+#define SERVER_LIST "browse.dat"
+
+/* the print command on the server, %s is replaced with the filename  */
+/* note that the -r removes the file after printing - you'll run out  */
+/* of disk pretty quickly if you don't. This command is only used as  */
+/* the default - it can be overridden in the configuration file.      */
+#define PRINT_COMMAND "lpr -r %s"
+
+/* the lpq command on the server. the printername is passed as an argument */
+#ifndef LPQ_COMMAND
+#define LPQ_COMMAND "lpq -P"
+#endif
+
+/* shall guest entries in printer queues get changed to user entries,
+   so they can be deleted using the windows print manager? */
+#define LPQ_GUEST_TO_USER
+
+/* 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 type of filesystem do we want this to show up as in a NT file
+   manager window? */
+#define FSTYPE_STRING "Samba"
+
+/* we have two time standards - local and GMT. This will try to sort them out.
+ */
+
+#define LOCAL_TO_GMT 1
+#define GMT_TO_LOCAL (-1)
+
+/* do you want smbd to send a 1 byte packet to nmbd to trigger it to start 
+   when smbd starts? */
+#ifndef PRIME_NMBD
+#define PRIME_NMBD 1
+#endif
+
+/* 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:
+
+   GUEST_SESSSETUP = 0 means session setups with an invalid password
+   are rejected.
+
+   GUEST_SESSSETUP = 1 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
+
+   GUEST_SESSSETUP = 2 means session setups with an invalid password
+   are treated as a guest login
+
+   Note that GUEST_SESSSETUP only has an effect in user or server
+   level security.
+   */
+#ifndef GUEST_SESSSETUP
+#define GUEST_SESSSETUP 0
+#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 UID_CACHE_SIZE 4
+
+/* 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 (10)
+#define SHARE_MODES_CHECK (10)
+#define SHARE_MODES_CLEAN (300)
+#define IDLE_CLOSED_TIMEOUT (60)
+#define DPTR_IDLE_TIMEOUT (120)
+#define SMBD_SELECT_LOOP (10)
+#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)
+
+/* 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
+
+/* 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
+
+#define SMB_ALIGNMENT 1
+
+
+/* shall we support browse requests via a FIFO to nmbd? */
+#define ENABLE_FIFO 1
+
+
+#endif
diff --git a/source/include/nameserv.h b/source/include/nameserv.h
new file mode 100644 (file)
index 0000000..168dd4b
--- /dev/null
@@ -0,0 +1,184 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   NBT netbios header - version 2
+   Copyright (C) Andrew Tridgell 1994-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.
+   
+*/
+
+#define MAX_DGRAM_SIZE 576
+#define MIN_DGRAM_SIZE 12
+
+#define NMB_PORT 137
+#define DGRAM_PORT 138
+#define SMB_PORT 139
+
+enum name_source {LMHOSTS, REGISTER, SELF, DNS, DNSFAIL};
+enum node_type {B_NODE=0, P_NODE=1, M_NODE=2, NBDD_NODE=3};
+enum packet_type {NMB_PACKET, DGRAM_PACKET};
+
+/* a netbios name structure */
+struct nmb_name {
+  char name[17];
+  char scope[64];
+  int name_type;
+};
+
+/* this is the structure used for the local netbios name list */
+struct name_record
+{
+  struct name_record *next;
+  struct name_record *prev;
+  struct nmb_name name;
+  time_t death_time;
+  struct in_addr ip;
+  BOOL unique;
+  enum name_source source;
+};
+
+/* this is used by the list of domains */
+struct domain_record
+{
+  struct domain_record *next;
+  struct domain_record *prev;
+  fstring name;
+  time_t lastannounce_time;
+  int announce_interval;
+  struct in_addr bcast_ip;
+};
+
+/* this is used to hold the list of servers in my domain */
+struct server_record
+{
+  struct server_record *next;
+  struct server_record *prev;
+  fstring name;
+  fstring comment;
+  uint32 servertype;
+  time_t death_time;  
+};
+
+/* 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 a 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;
+};
+
+
+/* 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;
+  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;
+};
+
+
+/* this defines a list of network interfaces */
+struct net_interface {
+  struct net_interface *next;
+  struct in_addr ip;
+  struct in_addr bcast;
+  struct in_addr netmask;
+};
+
+
+/* prototypes */
+void free_nmb_packet(struct nmb_packet *nmb);
+void free_packet(struct packet_struct *packet);
+struct packet_struct *read_packet(int fd,enum packet_type packet_type);
+BOOL send_packet(struct packet_struct *p);
+struct packet_struct *receive_packet(int fd,enum packet_type type,int timeout);
+void make_nmb_name(struct nmb_name *n,char *name,int type,char *this_scope);
+BOOL name_query(int fd,char *name,int name_type,
+                      BOOL bcast,BOOL recurse,
+                      struct in_addr to_ip, struct in_addr *ip,void (*fn)());
+BOOL name_status(int fd,char *name,int name_type,BOOL recurse,
+                struct in_addr to_ip,char *master,char *rname,
+                void (*fn)());
+BOOL send_mailslot_reply(char *mailslot,int fd,char *buf,int len,
+                        char *srcname,char *dstname,
+                        int src_type,int dest_type,
+                        struct in_addr dest_ip,
+                        struct in_addr src_ip);
+char *namestr(struct nmb_name *n);
diff --git a/source/include/smb.h b/source/include/smb.h
new file mode 100644 (file)
index 0000000..b7faffa
--- /dev/null
@@ -0,0 +1,1006 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   SMB parameters and setup
+   Copyright (C) Andrew Tridgell 1992-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.
+*/
+#ifndef _SMB_H
+#define _SMB_H
+
+#ifndef MAX_CONNECTIONS
+#define MAX_CONNECTIONS 127
+#endif
+
+#ifndef MAX_OPEN_FILES
+#define MAX_OPEN_FILES 50
+#endif
+
+#ifndef GUEST_ACCOUNT
+#define GUEST_ACCOUNT "nobody"
+#endif
+
+#define BUFFER_SIZE (0xFFFF)
+#define SAFETY_MARGIN 1024
+
+#ifndef EXTERN
+#      define EXTERN extern
+#endif
+
+#define False (0)
+#define True (1)
+#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)
+#define PTR_DIFF(p1,p2) ((ptrdiff_t)(((char *)(p1)) - (char *)(p2)))
+
+typedef int BOOL;
+
+/*
+   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.
+*/
+
+/* afs/stds.h defines int16 and int32 */
+#ifndef AFS_AUTH
+typedef short int16;
+typedef int int32;
+#endif
+
+#ifndef uint16
+typedef unsigned short uint16;
+#endif
+
+#ifndef uint32
+typedef unsigned int uint32;
+#endif
+
+#define SIZEOFWORD 2
+
+#ifndef DEF_CREATE_MASK
+#define DEF_CREATE_MASK (0755)
+#endif
+
+#ifndef DEFAULT_PIPE_TIMEOUT
+#define DEFAULT_PIPE_TIMEOUT 10000000 /* Ten seconds */
+#endif
+
+/* debugging code */
+#ifndef SYSLOG
+#define DEBUG(level,body) ((DEBUGLEVEL>=(level))?(Debug1 body):0)
+#else
+EXTERN int syslog_level;
+
+#define DEBUG(level,body) ((DEBUGLEVEL>=(level))? \
+                                                  (syslog_level = (level), Debug1 body):0)
+#endif
+
+#define DIR_STRUCT_SIZE 43
+
+/* these define all the command types recognised by the server - there
+are lots of gaps so probably there are some rare commands that are not
+implemented */
+
+#define pSETDIR '\377'
+
+/* these define the attribute byte as seen by DOS */
+#define aRONLY (1L<<0)
+#define aHIDDEN (1L<<1)
+#define aSYSTEM (1L<<2)
+#define aVOLID (1L<<3)
+#define aDIR (1L<<4)
+#define aARCH (1L<<5)
+
+/* 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
+
+/* 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) */
+
+/* SMB X/Open error codes for the ERRdos error class */
+#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 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 ERRbadshare 32 /* Share mode on file conflict with open mode */
+#define ERRlock 33 /* Lock request conflicts with existing lock */
+#define ERRfilexists 80 /* File in operation already exists */
+#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 ERROR_EAS_DIDNT_FIT 275 /* Extended attributes didn't fit */
+#define ERROR_EAS_NOT_SUPPORTED 282 /* Extended attributes not suppored */
+#define ERRunknownlevel 124
+#define ERRunknownipc 2142
+
+
+/* here's a special one from observing NT */
+#define ERRnoipc 66 /* don't support ipc */
+
+/* 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
+
+
+typedef char pstring[1024];
+typedef char fstring[128];
+typedef fstring string;
+
+typedef struct
+{
+  int size;
+  int mode;
+  int uid;
+  int gid;
+  /* these times are normally kept in GMT */
+  time_t mtime;
+  time_t atime;
+  time_t ctime;
+  pstring name;
+} file_info;
+
+
+/* Structure used when SMBwritebmpx is active */
+typedef struct
+        {
+       int   wr_total_written; /* So we know when to discard this */
+       int32 wr_timeout;
+       int32 wr_errclass;
+       int32 wr_error; /* Cached errors */
+       BOOL  wr_mode; /* write through mode) */
+       BOOL  wr_discard; /* discard all further data */
+        } write_bmpx_struct;
+
+typedef struct
+{
+  int cnum;
+  int fd;
+  int pos;
+  int size;
+  int mode;
+  char *mmap_ptr;
+  int mmap_size;
+  write_bmpx_struct *wbmpx_ptr;
+  time_t open_time;
+  BOOL open;
+  BOOL can_lock;
+  BOOL can_read;
+  BOOL can_write;
+  BOOL share_mode;
+  BOOL share_pending;
+  BOOL print_file;
+  BOOL modified;
+  char *name;
+} files_struct;
+
+
+struct uid_cache {
+  int entries;
+  int list[UID_CACHE_SIZE];
+};
+
+typedef struct
+{
+  int service;
+  BOOL force_user;
+  int uid; /* uid of user who *opened* this connection */
+  int gid; /* gid of user who *opened* this connection */
+  struct uid_cache uid_cache;
+  void *dirptr;
+  BOOL open;
+  BOOL printer;
+  BOOL ipc;
+  BOOL read_only;
+  BOOL admin_user;
+  char *dirpath;
+  char *connectpath;
+  char *origpath;
+  char *user; /* name of user who *opened* this connection */
+  /* following groups stuff added by ih */
+  /* This groups info is valid for the user that *opened* the connection */
+  int ngroups;
+  gid_t *groups;
+  int *igroups; /* an integer version - some OSes are broken :-( */
+  time_t lastused;
+  BOOL used;
+  int num_files_open;
+} connection_struct;
+
+
+typedef struct
+{
+  int uid; /* uid of a validated user */
+  int gid; /* gid of a validated user */
+  fstring name; /* name of a validated user */
+  BOOL guest;
+  /* following groups stuff added by ih */
+  /* This groups info is needed for when we become_user() for this uid */
+  int user_ngroups;
+  gid_t *user_groups;
+  int *user_igroups; /* an integer version - some OSes are broken :-( */
+} user_struct;
+
+
+enum {LPQ_QUEUED,LPQ_PAUSED,LPQ_SPOOLING,LPQ_PRINTING};
+
+typedef struct
+{
+  int job;
+  int size;
+  int status;
+  int priority;
+  time_t time;
+  char user[30];
+  char file[100];
+} print_queue_struct;
+
+enum {LPSTAT_OK, LPSTAT_STOPPED, LPSTAT_ERROR};
+
+typedef struct
+{
+  fstring message;
+  int status;
+}  print_status_struct;
+
+
+/* this is used for smbstatus */
+struct connect_record
+{
+  int magic;
+  int pid;
+  int cnum;
+  int uid;
+  int gid;
+  char name[24];
+  char addr[24];
+  char machine[128];
+  time_t start;
+};
+
+
+#define LOCKING_VERSION 2
+
+/* these are useful macros for checking validity of handles */
+#define VALID_FNUM(fnum)   (((fnum) >= 0) && ((fnum) < MAX_OPEN_FILES))
+#define OPEN_FNUM(fnum)    (VALID_FNUM(fnum) && Files[fnum].open)
+#define VALID_CNUM(cnum)   (((cnum) >= 0) && ((cnum) < MAX_CONNECTIONS))
+#define OPEN_CNUM(cnum)    (VALID_CNUM(cnum) && Connections[cnum].open)
+#define IS_IPC(cnum)       (VALID_CNUM(cnum) && Connections[cnum].ipc)
+#define FNUM_OK(fnum,c) (OPEN_FNUM(fnum) && (c)==Files[fnum].cnum)
+
+#define CHECK_FNUM(fnum,c) if (!FNUM_OK(fnum,c)) \
+                               return(ERROR(ERRDOS,ERRbadfid))
+#define CHECK_READ(fnum) if (!Files[fnum].can_read) \
+                               return(ERROR(ERRDOS,ERRbadaccess))
+#define CHECK_WRITE(fnum) if (!Files[fnum].can_write) \
+                               return(ERROR(ERRDOS,ERRbadaccess))
+#define CHECK_ERROR(fnum) if (HAS_CACHED_ERROR(fnum)) \
+                               return(CACHED_ERROR(fnum))
+
+/* translates a connection number into a service number */
+#define SNUM(cnum)         (Connections[cnum].service)
+
+/* access various service details */
+#define SERVICE(snum)      (lp_servicename(snum))
+#define PRINTCAP           (lp_printcapname())
+#define PRINTCOMMAND(snum) (lp_printcommand(snum))
+#define PRINTERNAME(snum)  (lp_printername(snum))
+#define CAN_WRITE(cnum)    (OPEN_CNUM(cnum) && !Connections[cnum].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(cnum)    (OPEN_CNUM(cnum) && lp_print_ok(SNUM(cnum)))
+#define POSTSCRIPT(cnum)   (OPEN_CNUM(cnum) && lp_postscript(SNUM(cnum)))
+#define MAP_HIDDEN(cnum)   (OPEN_CNUM(cnum) && lp_map_hidden(SNUM(cnum)))
+#define MAP_SYSTEM(cnum)   (OPEN_CNUM(cnum) && lp_map_system(SNUM(cnum)))
+#define MAP_ARCHIVE(cnum)   (OPEN_CNUM(cnum) && lp_map_archive(SNUM(cnum)))
+#define CREATE_MODE(cnum)  (lp_create_mode(SNUM(cnum)) | 0700)
+#ifdef SMB_PASSWD
+#define SMBENCRYPT()       (lp_encrypted_passwords())
+#else
+#define SMBENCRYPT() (False)
+#endif
+
+/* the basic packet size, assuming no words or bytes */
+#define smb_size 39
+
+/* offsets into message for common items */
+#define smb_com 8
+#define smb_rcls 9
+#define smb_reh 10
+#define smb_err 11
+#define smb_flg 13
+#define smb_flg2 14
+#define smb_reb 13
+#define smb_tid 28
+#define smb_pid 30
+#define smb_uid 32
+#define smb_mid 34
+#define smb_wct 36
+#define smb_vwv 37
+#define smb_vwv0 37
+#define smb_vwv1 39
+#define smb_vwv2 41
+#define smb_vwv3 43
+#define smb_vwv4 45
+#define smb_vwv5 47
+#define smb_vwv6 49
+#define smb_vwv7 51
+#define smb_vwv8 53
+#define smb_vwv9 55
+#define smb_vwv10 57
+#define smb_vwv11 59
+#define smb_vwv12 61
+#define smb_vwv13 63
+#define smb_vwv14 65
+#define smb_vwv15 67
+#define smb_vwv16 69
+#define smb_vwv17 71
+
+
+/* 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 /* Unlock a range then write */
+#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 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 */
+
+
+/* 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           10
+#define TRANSACT2_FINDNOTIFYFIRST 11
+#define TRANSACT2_FINDNOTIFYNEXT  12
+#define TRANSACT2_MKDIR           13
+
+
+/* these are the trans2 sub fields for primary requests */
+#define smb_tpscnt smb_vwv0
+#define smb_tdscnt smb_vwv1
+#define smb_mprcnt smb_vwv2
+#define smb_mdrcnt smb_vwv3
+#define smb_msrcnt smb_vwv4
+#define smb_flags smb_vwv5
+#define smb_timeout smb_vwv6
+#define smb_pscnt smb_vwv9
+#define smb_psoff smb_vwv10
+#define smb_dscnt smb_vwv11
+#define smb_dsoff smb_vwv12
+#define smb_suwcnt smb_vwv13
+#define smb_setup smb_vwv14
+#define smb_setup0 smb_setup
+#define smb_setup1 (smb_setup+2)
+#define smb_setup2 (smb_setup+4)
+
+/* these are for the secondary requests */
+#define smb_spscnt smb_vwv2
+#define smb_spsoff smb_vwv3
+#define smb_spsdisp smb_vwv4
+#define smb_sdscnt smb_vwv5
+#define smb_sdsoff smb_vwv6
+#define smb_sdsdisp smb_vwv7
+#define smb_sfid smb_vwv8
+
+/* and these for responses */
+#define smb_tprcnt smb_vwv0
+#define smb_tdrcnt smb_vwv1
+#define smb_prcnt smb_vwv3
+#define smb_proff smb_vwv4
+#define smb_prdisp smb_vwv5
+#define smb_drcnt smb_vwv6
+#define smb_droff smb_vwv7
+#define smb_drdisp smb_vwv8
+
+/* where to find the base of the SMB packet proper */
+#define smb_base(buf) (((char *)(buf))+4)
+
+
+#define SUCCESS 0  /* The request was successful. */
+#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. */
+
+/* structure used to hold the incoming hosts info */
+struct from_host {
+    char   *name;                      /* host name */
+    char   *addr;                      /* host address */
+    struct sockaddr_in *sin;           /* their side of the link */
+};
+
+/* and a few prototypes */
+BOOL user_ok(char *user,int snum);
+int sys_rename(char *from, char *to);
+int sys_select(fd_set *fds,struct timeval *tval);
+int sys_unlink(char *fname);
+int sys_open(char *fname,int flags,int mode);
+DIR *sys_opendir(char *dname);
+int sys_stat(char *fname,struct stat *sbuf);
+int sys_lstat(char *fname,struct stat *sbuf);
+int sys_mkdir(char *dname,int mode);
+int sys_rmdir(char *dname);
+int sys_chdir(char *dname);
+int sys_utime(char *fname,struct utimbuf *times);
+int sys_disk_free(char *path,int *bsize,int *dfree,int *dsize);
+void lpq_reset(int);
+void status_printjob(int cnum,int snum,int jobid,int status);
+void DirCacheAdd(char *path,char *name,char *dname,int snum);
+char *DirCacheCheck(char *path,char *name,int snum);
+void DirCacheFlush(int snum);
+int interpret_character_set(char *str, int def);
+char *dos2unix_format(char *, BOOL);
+char *unix2dos_format(char *, BOOL);
+BOOL fcntl_lock(int fd,int op,uint32 offset,uint32 count,int type);
+void BlockSignals(BOOL block);
+void msleep(int t);
+int file_lock(char *name,int timeout);
+void file_unlock(int fd);
+int find_service(char *service);
+int TvalDiff(struct timeval *tvalold,struct timeval *tvalnew);
+int smb_offset(char *p,char *buf);
+void sync_file(int fnum);
+int PutUniCode(char *dst,char *src);
+void map_username(char *user);
+void close_low_fds(void);
+void clean_share_files(void);
+int write_socket(int fd,char *buf,int len);
+char *readdirname(void *p);
+int dos_chmod(int cnum,char *fname,int mode,struct stat *st);
+int smb_numwords(char *buf);
+int get_share_mode(int cnum,struct stat *sbuf,int *pid);
+void del_share_mode(int fnum);
+BOOL set_share_mode(int fnum,int mode);
+int DSTDiff(time_t t);
+void TimeInit(void);
+void put_long_date(char *p,time_t t);
+time_t interpret_long_date(char *p);
+void dptr_idlecnum(int cnum);
+void dptr_closecnum(int cnum);
+void init_dptrs(void);
+void fault_setup();
+void set_socket_options(int fd, char *options);
+void putip(void *dest,void *src);
+void standard_sub_basic(char *s);
+void *OpenDir(char *name);
+void CloseDir(void *p);
+char *ReadDirName(void *p);
+BOOL SeekDir(void *p,int pos);
+int TellDir(void *p);
+int write_data(int fd,char *buffer,int N);
+BOOL server_cryptkey(char *buf);
+BOOL server_validate(char *buf);
+BOOL become_service(int cnum,BOOL do_chdir);
+BOOL snum_used(int snum);
+BOOL reload_services(BOOL test);
+void reopen_logs(void);
+int transfer_file(int infd,int outfd,int n,char *header,int headlen,int align);
+int str_checksum(char *s);
+time_t file_modtime(char *fname);
+BOOL do_match(char *str, char *regexp, int case_sig);
+BOOL is_a_socket(int fd);
+void _smb_setlen(char *buf,int len);
+void valid_initialise(void);
+BOOL is_8_3(char *fname);
+BOOL is_mangled(char *s);
+void standard_sub(int cnum,char *s);
+void del_printqueue(int cnum,int snum,int jobid);
+BOOL strisnormal(char *s);
+BOOL check_mangled_stack(char *s);
+int sys_chown(char *fname,int uid,int gid);
+int sys_chroot(char *dname);
+BOOL next_token(char **ptr,char *buff,char *sep);
+void invalidate_uid(int uid);
+char *fgets_slash(char *s,int maxlen,FILE *f);
+int read_udp_socket(int fd,char *buf,int len);
+void exit_server(char *reason);
+BOOL process_exists(int pid);
+BOOL chgpasswd(char *name,char *oldpass,char *newpass);
+void array_promote(char *array,int elsize,int element);
+void string_replace(char *s,char oldc,char newc);
+BOOL user_in_list(char *user,char *list);
+BOOL string_sub(char *s,char *pattern,char *insert);
+char *StrnCpy(char *dest,const char *src,int n);
+char *validated_username(int vuid);
+BOOL set_user_password(char *user,char *oldpass,char *newpass);
+int smb_buf_ofs(char *buf);
+char *skip_string(char *buf,int n);
+BOOL is_locked(int fnum,int cnum,uint32 count,uint32 offset);
+int read_file(int fnum,char *data,int pos,int mincnt,int maxcnt,int timeout,BOOL exact);
+int write_file(int fnum,char *data,int n);
+BOOL do_lock(int fnum,int cnum,uint32 count,uint32 offset,int *eclass,uint32 *ecode);
+int seek_file(int fnum,int pos);
+BOOL do_unlock(int fnum,int cnum,uint32 count,uint32 offset,int *eclass,uint32 *ecode);
+int get_printqueue(int snum,int cnum,print_queue_struct **queue,print_status_struct *status);
+void parse_connect(char *buf,char *service,char *user,char *password,int *pwlen,char *dev);
+int setup_groups(char *user,int uid, int gid, int *p_ngroups, 
+                int **p_igroups, gid_t **p_groups);
+int make_connection(char *service,char *user,char *password, int pwlen, char *dev,int vuid);
+char *dptr_path(int key);
+char *dptr_wcard(int key);
+BOOL dptr_set_wcard(int key, char *wcard);
+BOOL dptr_set_attr(int key, uint16 attr);
+uint16 dptr_attr(int key);
+void dptr_close(int key);
+void dptr_closepath(char *path,int pid);
+int dptr_create(int cnum,char *path, BOOL expect_close,int pid);
+BOOL dptr_fill(char *buf,unsigned int key);
+BOOL dptr_zero(char *buf);
+void *dptr_fetch(char *buf,int *num);
+void *dptr_fetch_lanman2(char *params,int dptr_num);
+BOOL get_dir_entry(int cnum,char *mask,int dirtype,char *fname,int *size,int *mode,time_t *date,BOOL check_descend);
+void open_file(int fnum,int cnum,char *fname,int flags,int mode);
+void open_file_shared(int fnum,int cnum,char *fname,int share_mode,int ofun,int mode,int *Access,int *action);
+void close_file(int fnum);
+int reply_trans2(char *inbuf,char *outbuf,int length,int bufsize);
+int reply_trans(char *inbuf,char *outbuf);
+char *ufc_crypt(char *key,char *salt);
+BOOL authorise_login(int snum,char *user,char *password, int pwlen, 
+                    BOOL *guest,BOOL *force,int vuid);
+void add_session_user(char *user);
+int valid_uid(int uid);
+user_struct *get_valid_user_struct(int uid);
+BOOL password_ok(char *user,char *password, int pwlen, struct passwd *pwd, BOOL nt_password);
+void register_uid(int uid,int gid,char *name,BOOL guest);
+BOOL fromhost(int sock,struct from_host *f);
+BOOL strhasupper(char *s);
+BOOL strhaslower(char *s);
+int disk_free(char *path,int *bsize,int *dfree,int *dsize);
+char *uidtoname(int uid);
+char *gidtoname(int gid);
+int get_share_mode_byname(int cnum,char *fname,int *pid);
+int get_share_mode_by_fnum(int cnum,int fnum,int *pid);
+BOOL check_file_sharing(int cnum,char *fname);
+char *StrCpy(char *dest,char *src);
+int unix_error_packet(char *inbuf,char *outbuf,int def_class,uint32 def_code,int line);
+time_t make_unix_date2(void *date_ptr);
+int cached_error_packet(char *inbuf,char *outbuf,int fnum,int line);
+mode_t unix_mode(int cnum,int dosmode);
+BOOL check_name(char *name,int cnum);
+int error_packet(char *inbuf,char *outbuf,int error_class,uint32 error_code,int line);
+int find_free_file(void );
+BOOL unix_convert(char *name,int cnum);
+void unix_convert_lanman2(char *s,char *home,BOOL case_is_sig);
+void print_file(int fnum);
+int read_smb_length(int fd,char *inbuf,int timeout);
+int read_predict(int fd,int offset,char *buf,char **ptr,int num);
+void invalidate_read_prediction(int fd);
+void do_read_prediction();
+BOOL claim_connection(int cnum,char *name,int max_connections,BOOL Clear);
+BOOL yield_connection(int cnum,char *name,int max_connections);
+int count_chars(char *s,char c);
+int smbrun(char *,char *);
+BOOL name_map_mangle(char *OutName,BOOL need83,int snum);
+struct hostent *Get_Hostbyname(char *name);
+struct passwd *Get_Pwnam(char *user,BOOL allow_change);
+void Abort(void);
+void *Realloc(void *p,int size);
+void smb_setlen(char *buf,int len);
+int set_message(char *buf,int num_words,int num_bytes,BOOL zero);
+BOOL check_access(int snum);
+BOOL in_group(gid_t group, int current_gid, int ngroups, int *groups);
+BOOL string_set(char **dest,char *src);
+BOOL string_init(char **dest,char *src);
+void string_free(char **s);
+char *attrib_string(int mode);
+void unix_format(char *fname);
+BOOL directory_exist(char *dname,struct stat *st);
+time_t make_unix_date3(void *date_ptr);
+void put_dos_date3(char *buf,int offset,time_t unixdate);
+void make_dir_struct(char *buf,char *mask,char *fname,unsigned int size,int mode,time_t date);
+BOOL in_list(char *s,char *list,BOOL case_sensitive);
+void strupper(char *s);
+BOOL file_exist(char *fname,struct stat *sbuf);
+int read_with_timeout(int fd,char *buf,int mincnt,int maxcnt, long time_out, BOOL exact);
+void close_sockets(void );
+BOOL send_smb(int fd,char *buffer);
+BOOL send_keepalive(int client);
+int read_data(int fd,char *buffer,int N);
+int smb_len(char *buf);
+BOOL receive_smb(int fd,char *buffer,int timeout);
+void show_msg(char *buf);
+BOOL big_endian(void );
+BOOL become_user(int cnum, int uid);
+BOOL unbecome_user(void);
+void become_daemon(void);
+BOOL reduce_name(char *s,char *dir,BOOL widelinks);
+void strlower(char *s);
+void strnorm(char *s);
+char *smb_buf(char *buf);
+char *smb_trans2_param(char *buf);
+char *smb_trans2_data(char *buf);
+BOOL strequal(char *,char *);
+BOOL strnequal(char *,char *,int n);
+BOOL strcsequal(char *,char *);
+BOOL mask_match( char *str, char *regexp, int case_sig, BOOL trans2);
+int dos_mode(int ,char *,struct stat *);
+char *timestring();
+BOOL ip_equal(struct in_addr ip1,struct in_addr ip2);
+BOOL send_one_packet(char *buf,int len,struct in_addr ip,int port,int type);
+char *get_home_dir(char *);
+int set_filelen(int fd, long len);
+void put_dos_date(char *buf,int offset,time_t unixdate);
+void put_dos_date2(char *buf,int offset,time_t unixdate);
+int lp_keepalive(void);
+int name_len(char *s);
+void dos_clean_name(char *s);
+void unix_clean_name(char *s);
+time_t make_unix_date(void *date_ptr);
+BOOL lanman2_match( char *str, char *regexp, int case_sig, BOOL autoext);
+BOOL trim_string(char *s,char *front,char *back);
+int byte_checksum(char *buf,int len);
+BOOL yesno(char *p);
+uint32 file_size(char *file_name);
+void dos_format(char *fname);
+char *GetWd(char *s);
+int name_mangle(char *in,char *out,char name_type);
+int name_len(char *s);
+void create_mangled_stack(int size);
+int name_extract(char *buf,int ofs,char *name);
+void get_broadcast(struct in_addr *if_ipaddr, struct in_addr *if_bcast, struct in_addr *if_nmask);
+BOOL allow_access(char *deny_list,char *allow_list,struct from_host *client);
+#ifdef __STDC__
+int Debug1(char *, ...);
+#else
+int Debug1();
+#endif
+BOOL check_hosts_equiv(char *user);
+int chain_reply(int type,char *inbuf,char *inbuf2,char *outbuf,char *outbuf2,int size,int bufsize);
+void close_cnum(int cnum,int uid);
+char *smb_errstr(char *inbuf);
+void GetTimeOfDay(struct timeval *tval);
+struct tm *LocalTime(time_t *t,int);
+int TimeDiff(time_t t);
+BOOL set_filetime(char *fname,time_t mtime);
+char *dirname_dos(char *path,char *buf);
+BOOL get_myname(char *myname,struct in_addr *ip);
+void expand_mask(char *Mask, BOOL);
+BOOL sane_unix_date(time_t unixdate);
+time_t start_of_month(void);
+char *smb_fn_name(int cnum);
+void get_machine_info(void);
+int open_socket_in(int type, int port, int dlevel);
+int open_socket_out(int type,struct in_addr *addr, int port );
+struct in_addr *interpret_addr2(char *str);
+BOOL zero_ip(struct in_addr ip);
+int read_max_udp(int fd,char *buffer,int bufsize,int maxtime);
+int interpret_protocol(char *str,int def);
+int interpret_security(char *str,int def);
+int ChDir(char *path);
+int smb_buflen(char *buf);
+unsigned long interpret_addr(char *str);
+void mangle_name_83(char *s);
+BOOL lp_casesignames(void);
+void setup_logging(char *pname,BOOL interactive);
+#ifdef DFS_AUTH
+void dfs_unlogin(void);
+extern int dcelogin_atmost_once;
+#endif
+#if AJT
+void ajt_panic(void);
+#endif
+#ifdef NOSTRDUP
+char *strdup(char *s);
+#endif
+#ifdef REPLACE_STRLEN
+int Strlen(char *);
+#endif
+#ifdef REPLACE_STRSTR
+char *Strstr(char *s, char *p);
+#endif
+
+#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
+
+#ifndef SIGNAL_CAST
+#define SIGNAL_CAST
+#endif
+
+#ifndef SELECT_CAST
+#define SELECT_CAST
+#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
+
+
+/* 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_ALTERNATE_XPORT     0x20000000  
+#define SV_TYPE_LOCAL_LIST_ONLY     0x40000000  
+#define SV_TYPE_DOMAIN_ENUM         0x80000000
+#define SV_TYPE_ALL                 0xFFFFFFFF  
+
+
+
+/* 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};
+
+/* printing types */
+enum printing_types {PRINT_BSD,PRINT_SYSV,PRINT_AIX,PRINT_HPUX,PRINT_QNX};
+
+
+/* case handling */
+enum case_handling {CASE_LOWER,CASE_UPPER};
+
+
+/* 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)))
+
+/* 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(fnum) (Files[(fnum)].open && \
+                               Files[(fnum)].wbmpx_ptr && \
+                               Files[(fnum)].wbmpx_ptr->wr_discard)
+/* Macro to turn the cached error into an error packet */
+#define CACHED_ERROR(fnum) cached_error_packet(inbuf,outbuf,fnum,__LINE__)
+
+/* these are the datagram types */
+#define DGRAM_DIRECT_UNIQUE 0x10
+
+#define ERROR(class,x) error_packet(inbuf,outbuf,class,x,__LINE__)
+
+/* this is how errors are generated */
+#define UNIXERROR(defclass,deferror) unix_error_packet(inbuf,outbuf,defclass,deferror,__LINE__)
+
+#define ROUNDUP(x,g) (((x)+((g)-1))&~((g)-1))
+
+#endif 
+/* _SMB_H */
diff --git a/source/include/trans2.h b/source/include/trans2.h
new file mode 100644 (file)
index 0000000..cc366cc
--- /dev/null
@@ -0,0 +1,241 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   SMB transaction2 handling
+   Copyright (C) Jeremy Allison 1994
+
+   Extensively modified by Andrew Tridgell, 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.
+*/
+
+#ifndef _TRANS2_H_
+#define _TRANS2_H_
+
+/* Define the structures needed for the trans2 calls. */
+
+/*******************************************************
+ For DosFindFirst/DosFindNext - level 1
+
+MAXFILENAMELEN = 255;
+FDATE == uint16
+FTIME == uint16
+ULONG == uint32
+USHORT == uint16
+
+typedef struct _FILEFINDBUF {
+Byte offset   Type     name                description
+-------------+-------+-------------------+--------------
+0             FDATE    fdateCreation;
+2             FTIME    ftimeCreation;
+4             FDATE    fdateLastAccess;
+6             FTIME    ftimeLastAccess;
+8             FDATE    fdateLastWrite;
+10            FTIME    ftimeLastWrite;
+12            ULONG    cbFile               file length in bytes
+16            ULONG    cbFileAlloc          size of file allocation unit
+20            USHORT   attrFile
+22            UCHAR    cchName              length of name to follow (not including zero)
+23            UCHAR    achName[MAXFILENAMELEN]; Null terminated name
+} FILEFINDBUF;
+*********************************************************/
+
+#define l1_fdateCreation 0
+#define l1_fdateLastAccess 4
+#define l1_fdateLastWrite 8
+#define l1_cbFile 12
+#define l1_cbFileAlloc 16
+#define l1_attrFile 20
+#define l1_cchName 22
+#define l1_achName 23
+
+/**********************************************************
+For DosFindFirst/DosFindNext - level 2
+
+typedef struct _FILEFINDBUF2 {
+Byte offset   Type     name                description
+-------------+-------+-------------------+--------------
+0             FDATE    fdateCreation;
+2             FTIME    ftimeCreation;
+4             FDATE    fdateLastAccess;
+6             FTIME    ftimeLastAccess;
+8             FDATE    fdateLastWrite;
+10            FTIME    ftimeLastWrite;
+12            ULONG    cbFile               file length in bytes
+16            ULONG    cbFileAlloc          size of file allocation unit
+20            USHORT   attrFile
+22            ULONG    cbList               Extended attribute list (always 0)
+26            UCHAR    cchName              length of name to follow (not including zero)
+27            UCHAR    achName[MAXFILENAMELEN]; Null terminated name
+} FILEFINDBUF2;
+*************************************************************/
+
+#define l2_fdateCreation 0
+#define l2_fdateLastAccess 4
+#define l2_fdateLastWrite 8
+#define l2_cbFile 12
+#define l2_cbFileAlloc 16
+#define l2_attrFile 20
+#define l2_cbList 22
+#define l2_cchName 26
+#define l2_achName 27
+
+
+/**********************************************************
+For DosFindFirst/DosFindNext - level 260
+
+typedef struct _FILEFINDBUF260 {
+Byte offset   Type     name                description
+-------------+-------+-------------------+--------------
+0              ULONG  NextEntryOffset;
+4              ULONG  FileIndex;
+8              LARGE_INTEGER CreationTime;
+16             LARGE_INTEGER LastAccessTime;
+24             LARGE_INTEGER LastWriteTime;
+32             LARGE_INTEGER ChangeTime;
+40             LARGE_INTEGER EndOfFile;
+48             LARGE_INTEGER AllocationSize;
+56             ULONG FileAttributes;
+60             ULONG FileNameLength;
+64             ULONG EaSize;
+68             CHAR ShortNameLength;
+70             UNICODE ShortName[12];
+94             UNICODE FileName[];
+*************************************************************/
+
+#define l260_achName 94
+
+
+/**********************************************************
+For DosQueryPathInfo/DosQueryFileInfo/DosSetPathInfo/
+DosSetFileInfo - level 1
+
+typedef struct _FILESTATUS {
+Byte offset   Type     name                description
+-------------+-------+-------------------+--------------
+0             FDATE    fdateCreation;
+2             FTIME    ftimeCreation;
+4             FDATE    fdateLastAccess;
+6             FTIME    ftimeLastAccess;
+8             FDATE    fdateLastWrite;
+10            FTIME    ftimeLastWrite;
+12            ULONG    cbFile               file length in bytes
+16            ULONG    cbFileAlloc          size of file allocation unit
+20            USHORT   attrFile
+} FILESTATUS;
+*************************************************************/
+
+/* Use the l1_ defines from DosFindFirst */
+
+/**********************************************************
+For DosQueryPathInfo/DosQueryFileInfo/DosSetPathInfo/
+DosSetFileInfo - level 2
+
+typedef struct _FILESTATUS2 {
+Byte offset   Type     name                description
+-------------+-------+-------------------+--------------
+0             FDATE    fdateCreation;
+2             FTIME    ftimeCreation;
+4             FDATE    fdateLastAccess;
+6             FTIME    ftimeLastAccess;
+8             FDATE    fdateLastWrite;
+10            FTIME    ftimeLastWrite;
+12            ULONG    cbFile               file length in bytes
+16            ULONG    cbFileAlloc          size of file allocation unit
+20            USHORT   attrFile
+22            ULONG    cbList               Length of EA's (0)
+} FILESTATUS2;
+*************************************************************/
+
+/* Use the l2_ #defines from DosFindFirst */
+
+/**********************************************************
+For DosQFSInfo/DosSetFSInfo - level 1
+
+typedef struct _FSALLOCATE {
+Byte offset   Type     name                description
+-------------+-------+-------------------+--------------
+0             ULONG    idFileSystem       id of file system
+4             ULONG    cSectorUnit        number of sectors per allocation unit
+8             ULONG    cUnit              number of allocation units
+12            ULONG    cUnitAvail         Available allocation units
+16            USHORT   cbSector           bytes per sector
+} FSALLOCATE;
+*************************************************************/
+
+#define l1_idFileSystem 0
+#define l1_cSectorUnit 4
+#define l1_cUnit 8
+#define l1_cUnitAvail 12
+#define l1_cbSector 16
+
+/**********************************************************
+For DosQFSInfo/DosSetFSInfo - level 2
+
+typedef struct _FSINFO {
+Byte offset   Type     name                description
+-------------+-------+-------------------+--------------
+0             FDATE   vol_fdateCreation
+2             FTIME   vol_ftimeCreation
+4             UCHAR   vol_cch             length of volume name (excluding NULL)
+5             UCHAR   vol_szVolLabel[12]  volume name
+} FSINFO;
+*************************************************************/
+
+#define SMB_QUERY_FS_LABEL_INFO                        0x101
+#define SMB_QUERY_FS_VOLUME_INFO               0x102
+#define SMB_QUERY_FS_SIZE_INFO                 0x103
+#define SMB_QUERY_FS_DEVICE_INFO               0x104
+#define SMB_QUERY_FS_ATTRIBUTE_INFO            0x105
+
+
+#define l2_vol_fdateCreation 0
+#define l2_vol_cch 4
+#define l2_vol_szVolLabel 5
+
+
+#define SMB_QUERY_FILE_BASIC_INFO      0x101
+#define SMB_QUERY_FILE_STANDARD_INFO   0x102
+#define SMB_QUERY_FILE_EA_INFO         0x103
+#define SMB_QUERY_FILE_NAME_INFO       0x104
+#define SMB_QUERY_FILE_ALLOCATION_INFO 0x105
+#define SMB_QUERY_FILE_END_OF_FILEINFO 0x106
+#define SMB_QUERY_FILE_ALL_INFO                0x107
+#define SMB_QUERY_FILE_ALT_NAME_INFO   0x108
+#define SMB_QUERY_FILE_STREAM_INFO     0x109
+
+#define SMB_FIND_FILE_DIRECTORY_INFO           0x101
+#define SMB_FIND_FILE_FULL_DIRECTORY_INFO      0x102
+#define SMB_FIND_FILE_NAMES_INFO               0x103
+#define SMB_FIND_FILE_BOTH_DIRECTORY_INFO      0x104
+
+#define SMB_SET_FILE_BASIC_INFO                0x101
+#define SMB_SET_FILE_DISPOSITION_INFO  0x102
+#define SMB_SET_FILE_ALLOCATION_INFO   0x103
+#define SMB_SET_FILE_END_OF_FILE_INFO  0x104
+
+#define DIRLEN_GUESS (45+MAX(l1_achName,l2_achName))
+
+/* Function prototypes */
+
+
+int reply_findnclose(char *inbuf,char *outbuf,int length,int bufsize);
+
+int reply_findclose(char *inbuf,char *outbuf,int length,int bufsize);
+
+#endif
+
+
+
diff --git a/source/include/version.h b/source/include/version.h
new file mode 100644 (file)
index 0000000..9ad8b7d
--- /dev/null
@@ -0,0 +1 @@
+#define VERSION "1.9.16alpha1"
diff --git a/source/include/vt_mode.h b/source/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/source/lib/access.c b/source/lib/access.c
new file mode 100644 (file)
index 0000000..14a84b2
--- /dev/null
@@ -0,0 +1,389 @@
+/* 
+This module is an adaption of code from the tcpd-1.4 package written
+by Wietse Venema, Eindhoven University of Technology, The Netherlands.
+
+The code is used here with permission.
+
+The code has been considerably changed from the original. Bug reports
+should be sent to Andrew.Tridgell@anu.edu.au
+*/
+
+#include "includes.h"
+#include "loadparm.h"
+
+#define ALLOW_PURE_ADDRESSES
+
+extern int DEBUGLEVEL;
+
+#ifndef        INADDR_NONE
+#define        INADDR_NONE     ((unsigned long)~0)
+#endif
+
+
+#define FROM_ADDRLEN  (4*3+3+1)
+#define Good True
+#define Bad False
+
+#define CLIENT_MATCH client_match
+
+/* Delimiters for lists of daemons or clients. */
+
+static char sep[] = ", \t";
+
+/* Constants to be used in assignments only, not in comparisons... */
+
+#define        YES             1
+#define        NO              0
+#define        FAIL            (-1)
+
+/* Forward declarations. */
+BOOL allow_access(char *deny_list,char *allow_list,struct from_host *client);
+static int list_match(char *list,char *item, int (*match_fn)());
+static int client_match(char *tok,char *item);
+static int string_match(char *tok,char *s);
+static int masked_match(char *tok, char *slash, char *s);
+static int matchname(char *remotehost,struct in_addr  addr);
+BOOL fromhost(int sock,struct from_host *f);
+
+
+/* Size of logical line buffer. */
+#define        BUFLEN 2048
+
+
+/* return true if access should be allowed to a service*/
+BOOL check_access(int snum)
+{
+  extern int Client;
+  extern struct from_host Client_info;
+  char *denyl,*allowl;
+  BOOL ret = False;
+
+  denyl = lp_hostsdeny(snum);
+  if (denyl) denyl = strdup(denyl);
+
+  allowl = lp_hostsallow(snum);
+  if (allowl) allowl = strdup(allowl);
+
+
+  fromhost(Client,&Client_info);
+
+  if ((!denyl || *denyl==0) && (!allowl || *allowl==0))
+    ret = True;
+
+  if (!ret)
+    {
+      if (!fromhost(Client,&Client_info))
+       DEBUG(0,("ERROR: Can't get from_host info\n"));
+      else
+       {
+         if (allow_access(denyl,allowl,&Client_info))
+           {
+             if (snum >= 0)
+               DEBUG(2,("Allowed connection from %s (%s) to %s\n",
+                        Client_info.name,Client_info.addr,
+                        lp_servicename(snum)));
+             ret = True;
+           }
+         else
+           if (snum >= 0)
+             DEBUG(0,("Denied connection from %s (%s) to %s\n",
+                      Client_info.name,Client_info.addr,
+                      lp_servicename(snum)));
+       }
+    }
+
+  if (denyl) free(denyl);
+  if (allowl) free(allowl);
+  return(ret);
+}
+
+
+/* return true if access should be allowed */
+BOOL allow_access(char *deny_list,char *allow_list,struct from_host *client)
+{
+  /* if theres no deny list and no allow list then allow access */
+  if ((!deny_list || *deny_list == 0) && (!allow_list || *allow_list == 0))
+    return(True);  
+
+  /* if there is an allow list but no deny list then allow only hosts
+     on the allow list */
+  if (!deny_list || *deny_list == 0)
+    return(list_match(allow_list,(char *)client,CLIENT_MATCH));
+
+  /* if theres a deny list but no allow list then allow
+     all hosts not on the deny list */
+  if (!allow_list || *allow_list == 0)
+    return(!list_match(deny_list,(char *)client,CLIENT_MATCH));
+
+  /* if there are both type of list then allow all hosts on the allow list */
+  if (list_match(allow_list,(char *)client,CLIENT_MATCH))
+    return (True);
+
+  /* if there are both type of list and it's not on the allow then
+     allow it if its not on the deny */
+  if (list_match(deny_list,(char *)client,CLIENT_MATCH))
+    return (False);
+
+  return (True);
+}
+
+/* list_match - match an item against a list of tokens with exceptions */
+/* (All modifications are marked with the initials "jkf") */
+static int list_match(char *list,char *item, int (*match_fn)())
+{
+    char   *tok;
+    char   *listcopy;          /* jkf */
+    int     match = NO;
+
+    /*
+     * jkf@soton.ac.uk -- 31 August 1994 -- Stop list_match()
+     * overwriting the list given as its first parameter.
+     */
+
+    /* jkf -- can get called recursively with NULL list */
+    listcopy = (list == 0) ? (char *)0 : strdup(list);
+
+    /*
+     * Process tokens one at a time. We have exhausted all possible matches
+     * when we reach an "EXCEPT" token or the end of the list. If we do find
+     * a match, look for an "EXCEPT" list and recurse to determine whether
+     * the match is affected by any exceptions.
+     */
+
+    for (tok = strtok(listcopy, sep); tok ; tok = strtok(NULL, sep)) {
+       if (strcasecmp(tok, "EXCEPT") == 0)     /* EXCEPT: give up */
+           break;
+       if ((match = (*match_fn) (tok, item)))  /* YES or FAIL */
+           break;
+    }
+    /* Process exceptions to YES or FAIL matches. */
+
+    if (match != NO) {
+       while ((tok = strtok((char *) 0, sep)) && strcasecmp(tok, "EXCEPT"))
+            /* VOID */ ;
+       if (tok == 0 || list_match((char *) 0, item, match_fn) == NO) {
+           if (listcopy != 0) free(listcopy); /* jkf */
+           return (match);
+       }
+    }
+
+    if (listcopy != 0) free(listcopy); /* jkf */
+    return (NO);
+}
+
+
+/* client_match - match host name and address against token */
+static int client_match(char *tok,char *item)
+{
+    struct from_host *client = (struct from_host *) item;
+    int     match;
+
+    /*
+     * Try to match the address first. If that fails, try to match the host
+     * name if available.
+     */
+
+    if ((match = string_match(tok, client->addr)) == 0)
+       if (client->name[0] != 0)
+           match = string_match(tok, client->name);
+    return (match);
+}
+
+/* string_match - match string against token */
+static int string_match(char *tok,char *s)
+{
+    int     tok_len;
+    int     str_len;
+    char   *cut;
+
+    /*
+     * Return YES if a token has the magic value "ALL". Return FAIL if the
+     * token is "FAIL". If the token starts with a "." (domain name), return
+     * YES if it matches the last fields of the string. If the token has the
+     * magic value "LOCAL", return YES if the string does not contain a "."
+     * character. If the token ends on a "." (network number), return YES if
+     * it matches the first fields of the string. If the token begins with a
+     * "@" (netgroup name), return YES if the string is a (host) member of
+     * the netgroup. Return YES if the token fully matches the string. If the
+     * token is a netnumber/netmask pair, return YES if the address is a
+     * member of the specified subnet.
+     */
+
+    if (tok[0] == '.') {                       /* domain: match last fields */
+       if ((str_len = strlen(s)) > (tok_len = strlen(tok))
+           && strcasecmp(tok, s + str_len - tok_len) == 0)
+           return (YES);
+    } else if (tok[0] == '@') {                        /* netgroup: look it up */
+#ifdef NETGROUP
+      static char *mydomain = NULL;
+      char *hostname = NULL;
+      BOOL netgroup_ok = False;
+
+      if (!mydomain) yp_get_default_domain(&mydomain);
+
+      if (!(hostname = strdup(s))) {
+       DEBUG(1,("out of memory for strdup!\n"));
+       return NO;
+      }
+
+      netgroup_ok = innetgr(tok + 1, hostname, (char *) 0, mydomain);
+
+      DEBUG(5,("looking for %s of domain %s in netgroup %s gave %s\n", 
+              hostname,
+              mydomain, 
+              tok+1,
+              BOOLSTR(netgroup_ok)));
+
+#ifdef NETGROUP_INSECURE
+      /* if you really want netgroups that match non qualified names
+        then define NETGROUP_INSECURE. It can, however, be a big
+        security hole */
+      {
+       char        *clnt_domain;
+       if (!netgroup_ok && (clnt_domain=strchr(hostname,'.'))) {
+         *clnt_domain++ = '\0';
+         netgroup_ok = innetgr(tok + 1, hostname, (char *) 0, mydomain);
+       }
+      }
+#endif
+
+      free(hostname);
+      
+      if (netgroup_ok) return(YES);
+#else
+      DEBUG(0,("access: netgroup support is not configured"));
+      return (NO);
+#endif
+    } else if (strcasecmp(tok, "ALL") == 0) {  /* all: match any */
+       return (YES);
+    } else if (strcasecmp(tok, "FAIL") == 0) { /* fail: match any */
+       return (FAIL);
+    } else if (strcasecmp(tok, "LOCAL") == 0) {        /* local: no dots */
+       if (strchr(s, '.') == 0 && strcasecmp(s, "unknown") != 0)
+           return (YES);
+    } else if (!strcasecmp(tok, s)) {  /* match host name or address */
+       return (YES);
+    } else if (tok[(tok_len = strlen(tok)) - 1] == '.') {      /* network */
+       if (strncmp(tok, s, tok_len) == 0)
+           return (YES);
+    } else if ((cut = strchr(tok, '/')) != 0) {        /* netnumber/netmask */
+       if (isdigit(s[0]) && masked_match(tok, cut, s))
+           return (YES);
+    }
+    return (NO);
+}
+
+/* masked_match - match address against netnumber/netmask */
+static int masked_match(char *tok, char *slash, char *s)
+{
+    unsigned long net;
+    unsigned long mask;
+    unsigned long addr;
+
+    if ((addr = interpret_addr(s)) == INADDR_NONE)
+       return (NO);
+    *slash = 0;
+    net = interpret_addr(tok);
+    *slash = '/';
+    if (net == INADDR_NONE || (mask = interpret_addr(slash + 1)) == INADDR_NONE) {
+       DEBUG(0,("access: bad net/mask access control: %s", tok));
+       return (NO);
+    }
+    return ((addr & mask) == net);
+}
+
+
+/* fromhost - find out what is at the other end of a socket */
+BOOL fromhost(int sock,struct from_host *f)
+{
+    static struct sockaddr sa;
+    struct sockaddr_in *sockin = (struct sockaddr_in *) (&sa);
+    struct hostent *hp;
+    int     length = sizeof(sa);
+    static char addr_buf[FROM_ADDRLEN];
+    static char name_buf[MAXHOSTNAMELEN];
+    BOOL   takeAddressAsHostname = False;
+
+    if (getpeername(sock, &sa, &length) < 0) 
+      {
+       DEBUG(0,("getpeername failed\n"));
+       return(False);
+      }
+
+    f->sin = sockin;
+    f->addr = strcpy(addr_buf,(char *)inet_ntoa(sockin->sin_addr));
+
+    /* Look up the remote host name. */
+    if ((hp = gethostbyaddr((char *) &sockin->sin_addr,
+                           sizeof(sockin->sin_addr),
+                           AF_INET)) == 0) {
+      DEBUG(1,("Gethostbyaddr failed for %s\n",addr_buf));
+#ifdef ALLOW_PURE_ADDRESSES
+      takeAddressAsHostname = True;
+#else
+      return(False);
+#endif
+    }
+
+    /* Save the host name. A later gethostbyxxx() call may clobber it. */
+    f->name = StrnCpy(name_buf,
+                      takeAddressAsHostname? f->addr : hp->h_name,
+                      sizeof(name_buf) - 1);
+
+    /*
+     * Verify that the host name does not belong to someone else. If host
+     * name verification fails, pretend that the host name lookup failed.
+     */
+    if (!takeAddressAsHostname && !matchname(f->name, sockin->sin_addr))
+      {
+       DEBUG(0,("Matchname failed\n"));
+       return(False);
+      }
+
+    return(True);      
+}
+
+/* matchname - determine if host name matches IP address */
+static int matchname(char *remotehost,struct in_addr  addr)
+{
+  struct hostent *hp;
+  int     i;
+  
+  if ((hp = Get_Hostbyname(remotehost)) == 0) {
+    DEBUG(0,("Get_Hostbyname(%s): lookup failure", remotehost));
+    return (Bad);
+  } 
+
+    /*
+     * 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",
+         remotehost, hp->h_name));
+    return (Bad);
+  }
+       
+  /* 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], (caddr_t) & addr, sizeof(addr)) == 0)
+      return (Good);
+  }
+
+  /*
+   * 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",
+       inet_ntoa(addr), hp->h_name));
+  return (Bad);
+}
+
+
diff --git a/source/lib/charcnv.c b/source/lib/charcnv.c
new file mode 100644 (file)
index 0000000..049390f
--- /dev/null
@@ -0,0 +1,126 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   Character set conversion Extensions
+   Copyright (C) Andrew Tridgell 1992-1994
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public 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 DEBUGLEVEL;
+
+static char cvtbuf[1024];
+
+static mapsinited = 0;
+
+static char unix2dos[256];
+static char dos2unix[256];
+
+static void initmaps() {
+    int k;
+
+    for (k = 0; k < 256; k++) unix2dos[k] = k;
+    for (k = 0; k < 256; k++) dos2unix[k] = k;
+
+    mapsinited = 1;
+}
+
+static void update_map(char * str) {
+    char *p;
+
+    for (p = str; *p; p++) {
+        if (p[1]) {
+            unix2dos[(unsigned char)*p] = p[1];
+            dos2unix[(unsigned char)p[1]] = *p;
+            p++;
+        }
+    }
+}
+
+static void initiso() {
+
+    if (!mapsinited) initmaps();
+
+    update_map("\241\255\242\233\243\234\244\236\245\235\246\272\247\025\250\251");
+    update_map("\251\273\252\246\253\256\254\252\255\274\256\310\257\257\260\370");
+    update_map("\261\361\262\375\263\264\264\265\265\266\266\024\267\371\270\267");
+    update_map("\271\270\272\247\273\275\274\254\275\253\276\276\277\250\200\277");
+    update_map("\301\300\302\301\303\302\304\216\305\217\306\222\307\200\310\303");
+    update_map("\311\220\312\305\313\306\314\307\315\315\316\317\317\320\320\311");
+    update_map("\321\245\322\321\323\322\324\323\325\324\326\231\327\312\330\325");
+    update_map("\331\326\332\327\333\330\334\232\335\313\336\314\337\341\340\205");
+    update_map("\341\240\342\203\343\331\344\204\345\206\346\221\347\207\350\212");
+    update_map("\351\202\352\210\353\211\354\215\355\241\356\214\357\213\360\316");
+    update_map("\361\244\362\225\363\242\364\223\365\332\366\224\367\366\370\362");
+    update_map("\371\227\372\243\373\226\374\201\375\304\376\263\377\230");
+}
+
+/*
+ * Convert unix to dos
+ */
+char *
+unix2dos_format(char *str,BOOL overwrite)
+{
+    char *p;
+    char *dp;
+
+    if (!mapsinited) initmaps();
+    if (overwrite) {
+        for (p = str; *p; p++) *p = unix2dos[(unsigned char)*p];
+        return str;
+    } else {
+        for (p = str, dp = cvtbuf; *p; p++,dp++) *dp = unix2dos[(unsigned char)*p];
+        *dp = 0;
+        return cvtbuf;
+    }
+}
+
+/*
+ * Convert dos to unix
+ */
+char *
+dos2unix_format (char *str, BOOL overwrite)
+{
+    char *p;
+    char *dp;
+
+    if (!mapsinited) initmaps();
+    if (overwrite) {
+        for (p = str; *p; p++) *p = dos2unix[(unsigned char)*p];
+        return str;
+    } else {
+        for (p = str, dp = cvtbuf; *p; p++,dp++) *dp = dos2unix[(unsigned char)*p];
+        *dp = 0;
+        return cvtbuf;
+    }
+}
+
+
+/*
+ * Interpret character set.
+ */
+int 
+interpret_character_set (char *str, int def)
+{
+
+    if (strequal (str, "iso8859-1")) {
+        initiso();
+        return def;
+    } else {
+        DEBUG(0,("unrecognized character set\n"));
+    }
+    return def;
+}
diff --git a/source/lib/charset.c b/source/lib/charset.c
new file mode 100644 (file)
index 0000000..ada3ef7
--- /dev/null
@@ -0,0 +1,111 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   Character set handling
+   Copyright (C) Andrew Tridgell 1992-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.
+*/
+
+#define CHARSET_C
+#include "includes.h"
+
+extern int DEBUGLEVEL;
+
+char xx_dos_char_map[256];
+char xx_upper_char_map[256];
+char xx_lower_char_map[256];
+
+char *dos_char_map = NULL;
+char *upper_char_map = NULL;
+char *lower_char_map = NULL;
+
+static void add_dos_char(int lower, int upper)
+{
+  DEBUG(6,("Adding chars 0%o 0%o\n",lower,upper));
+  if (lower) dos_char_map[(char)lower] = 1;
+  if (upper) dos_char_map[(char)upper] = 1;
+  if (lower && upper) {
+    lower_char_map[(char)upper] = (char)lower;
+    upper_char_map[(char)lower] = (char)upper;
+  }
+}
+
+/****************************************************************************
+initialise the charset arrays
+****************************************************************************/
+void charset_initialise(void)
+{
+  int i;
+
+  dos_char_map = &xx_dos_char_map[128];
+  upper_char_map = &xx_upper_char_map[128];
+  lower_char_map = &xx_lower_char_map[128];
+
+  for (i= -128;i<=127;i++) {
+    dos_char_map[(char)i] = 0;
+  }
+
+  for (i=0;i<=127;i++) {
+    if (isalnum((char)i) || strchr("._^$~!#%&-{}()@'`",(char)i))
+      add_dos_char(i,0);
+  }
+
+  for (i= -128;i<=127;i++) {
+    char c = (char)i;
+    upper_char_map[i] = lower_char_map[i] = c;
+    if (isupper(c)) lower_char_map[c] = tolower(c);
+    if (islower(c)) upper_char_map[c] = toupper(c);
+  }
+
+  /* valid for all DOS PC */
+  add_dos_char(142,0);     /* A trema      */
+  add_dos_char(143,0);     /* A o          */
+  add_dos_char(144,0);     /* E '          */
+  add_dos_char(146,0);     /* AE           */
+  add_dos_char(153,0);     /* O trema      */
+  add_dos_char(154,0);     /* U trema      */
+  add_dos_char(165,0);     /* N tilda      */
+  add_dos_char(128,0);     /* C cedille    */
+  add_dos_char(156,0);     /* Pound        */
+  add_dos_char(183,0);     /* A `     (WIN)*/
+  add_dos_char(157,0);     /* Phi     (WIN)*/
+  add_dos_char(212,0);     /* E`      (WIN)*/
+}
+
+
+/*******************************************************************
+add characters depending on a string passed by the user
+********************************************************************/
+void add_char_string(char *s)
+{
+  char *extra_chars = (char *)strdup(s);
+  char *t;
+  if (!extra_chars) return;
+
+  for (t=strtok(extra_chars," \t\r\n"); t; t=strtok(NULL," \t\r\n")) {
+    char c1=0,c2=0;
+    int i1=0,i2=0;
+    if (isdigit(*t) || (*t)=='-') {
+      sscanf(t,"%i:%i",&i1,&i2);
+      add_dos_char(i1,i2);
+    } else {
+      sscanf(t,"%c:%c",&c1,&c2);
+      add_dos_char(c1,c2);
+    }
+  }
+
+  free(extra_chars);
+}
diff --git a/source/lib/fault.c b/source/lib/fault.c
new file mode 100644 (file)
index 0000000..20c75f7
--- /dev/null
@@ -0,0 +1,86 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   Critical Fault handling
+   Copyright (C) Andrew Tridgell 1992-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.
+*/
+
+#ifdef LINUX
+#define __KERNEL__
+#endif
+
+#include "includes.h"
+extern int DEBUGLEVEL;
+
+
+static void (*cont_fn)();
+
+
+/*******************************************************************
+report a fault
+********************************************************************/
+static void fault_report(int sig)
+{
+  DEBUG(0,("===============================================================\n"));
+  DEBUG(0,("INTERNAL ERROR: Signal %d in pid %d (%s)",sig,(int)getpid(),VERSION));
+  DEBUG(0,("\nPlease read the file BUGS.txt in the distribution\n"));
+  DEBUG(0,("===============================================================\n"));
+  
+#if AJT
+  ajt_panic();
+#endif  
+
+  if (cont_fn)
+    {
+      fault_setup(cont_fn);
+      cont_fn(NULL);
+#ifdef SIGSEGV
+      signal(SIGSEGV,SIGNAL_CAST SIG_DFL);
+#endif
+#ifdef SIGBUS
+      signal(SIGBUS,SIGNAL_CAST SIG_DFL);
+#endif
+      return; /* this should cause a core dump */
+    }
+  exit(1);
+}
+
+/****************************************************************************
+catch serious errors
+****************************************************************************/
+static void sig_fault(int sig)
+{
+  fault_report(sig);
+}
+
+/*******************************************************************
+setup our fault handlers
+********************************************************************/
+void fault_setup(void (*fn)())
+{
+  cont_fn = fn;
+
+#ifdef SIGSEGV
+  signal(SIGSEGV,SIGNAL_CAST sig_fault);
+#endif
+#ifdef SIGBUS
+  signal(SIGBUS,SIGNAL_CAST sig_fault);
+#endif
+}
+
+
+
diff --git a/source/lib/getsmbpass.c b/source/lib/getsmbpass.c
new file mode 100644 (file)
index 0000000..07a7dbf
--- /dev/null
@@ -0,0 +1,166 @@
+/* Copyright (C) 1992, 1993, 1994 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
+
+int tcgetattr(int fd, struct termio *t)
+{
+       return ioctl(fd, TCGETA, t);
+}
+
+int tcsetattr(int fd, int flags, const struct termio *t)
+{
+       if(flags & TCSAFLUSH)
+               ioctl(fd, TCFLSH, TCIOFLUSH);
+       return ioctl(fd, TCSETS, t);
+}
+
+#else /* SYSV_TERMIO */
+#ifdef BSD_TERMIO
+
+/* 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)
+
+#ifndef TCSAFLUSH
+#define TCSAFLUSH 1
+#endif
+
+#ifndef TCSANOW
+#define TCSANOW 0
+#endif
+
+int tcgetattr(int fd, struct sgttyb *t)
+{
+       return ioctl(fd, TIOCGETP, (char *)t);
+}
+
+int tcsetattr(int fd, int flags, const struct sgttyb *t)
+{
+       return ioctl(fd, TIOCSETP, (char *)t);
+}
+
+#else /* BSD_TERMIO */
+
+/* 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 /* BSD_TERMIO */
+#endif /* SYSV_TERMIO */
+
+char *
+getsmbpass(char *prompt)     
+{
+  FILE *in, *out;
+  int echo_off;
+  static char buf[256];
+  static size_t bufsize = sizeof(buf);
+  size_t nread;
+
+  /* Catch problematic signals */
+  signal(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 */
+  signal(SIGINT, SIGNAL_CAST SIG_DFL);
+
+  printf("\n");
+  return buf;
+}
+
+#else
+
+void getsmbpasswd_dummy() {;}
+#endif
diff --git a/source/lib/kanji.c b/source/lib/kanji.c
new file mode 100644 (file)
index 0000000..0af476e
--- /dev/null
@@ -0,0 +1,895 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   Kanji Extensions
+   Copyright (C) Andrew Tridgell 1992-1994
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+   Adding for Japanese language by <fujita@ainix.isac.co.jp> 1994.9.5
+     and extend coding system to EUC/SJIS/JIS/HEX at 1994.10.11
+     and add all jis codes sequence type at 1995.8.16
+     Notes: Hexadecimal code by <ohki@gssm.otuka.tsukuba.ac.jp>
+*/
+#ifdef KANJI
+
+#define _KANJI_C_
+#include "includes.h"
+
+/* coding system keep in */
+int coding_system = SJIS_CODE;
+
+/* jis si/so sequence */
+char jis_kso = JIS_KSO;
+char jis_ksi = JIS_KSI;
+char hex_tag = HEXTAG;
+
+/*******************************************************************
+  SHIFT JIS functions
+********************************************************************/
+/*******************************************************************
+ search token from S1 separated any char of S2
+ S1 contain SHIFT JIS chars.
+********************************************************************/
+char *
+sj_strtok (char *s1, const char *s2)
+{
+    static char *s = NULL;
+    char *q;
+    if (!s1) {
+       if (!s) {
+           return NULL;
+       }
+       s1 = s;
+    }
+    for (q = s1; *s1; ) {
+       if (is_shift_jis (*s1)) {
+           s1 += 2;
+       } else if (is_kana (*s1)) {
+           s1++;
+       } else {
+           char *p = strchr (s2, *s1);
+           if (p) {
+               if (s1 != q) {
+                   s = s1 + 1;
+                   *s1 = '\0';
+                   return q;
+               }
+               q = s1 + 1;
+           }
+           s1++;
+       }
+    }
+    s = NULL;
+    if (*q) {
+       return q;
+    }
+    return NULL;
+}
+
+/*******************************************************************
+ search string S2 from S1
+ S1 contain SHIFT JIS chars.
+********************************************************************/
+char *
+sj_strstr (const char *s1, const char *s2)
+{
+    register int len = strlen ((char *) s2);
+    if (!*s2) 
+       return (char *) s1;
+    for (;*s1;) {
+       if (*s1 == *s2) {
+           if (strncmp (s1, s2, len) == 0)
+               return (char *) s1;
+       }
+       if (is_shift_jis (*s1)) {
+           s1 += 2;
+       } else {
+           s1++;
+       }
+    }
+    return 0;
+}
+
+/*******************************************************************
+ Search char C from beginning of S.
+ S contain SHIFT JIS chars.
+********************************************************************/
+char *
+sj_strchr (const char *s, int c)
+{
+    for (; *s; ) {
+       if (*s == c)
+           return (char *) s;
+       if (is_shift_jis (*s)) {
+           s += 2;
+       } else {
+           s++;
+       }
+    }
+    return 0;
+}
+
+/*******************************************************************
+ Search char C end of S.
+ S contain SHIFT JIS chars.
+********************************************************************/
+char *
+sj_strrchr (const char *s, int c)
+{
+    register char *q;
+
+    for (q = 0; *s; ) {
+       if (*s == c) {
+           q = (char *) s;
+       }
+       if (is_shift_jis (*s)) {
+           s += 2;
+       } else {
+           s++;
+       }
+    }
+    return q;
+}
+
+/*******************************************************************
+  Code conversion
+********************************************************************/
+/* convesion buffer */
+static char cvtbuf[1024];
+
+/*******************************************************************
+  EUC <-> SJIS
+********************************************************************/
+static int
+euc2sjis (register int hi, register int lo)
+{
+    if (hi & 1)
+       return ((hi / 2 + (hi < 0xdf ? 0x31 : 0x71)) << 8) |
+           (lo - (lo >= 0xe0 ? 0x60 : 0x61));
+    else
+       return ((hi / 2 + (hi < 0xdf ? 0x30 : 0x70)) << 8) | (lo - 2);
+}
+
+static int
+sjis2euc (register int hi, register int lo)
+{
+    if (lo >= 0x9f)
+       return ((hi * 2 - (hi >= 0xe0 ? 0xe0 : 0x60)) << 8) | (lo + 2);
+    else
+       return ((hi * 2 - (hi >= 0xe0 ? 0xe1 : 0x61)) << 8) |
+           (lo + (lo >= 0x7f ? 0x60 : 0x61));
+}
+
+/*******************************************************************
+ Convert FROM contain SHIFT JIS codes to EUC codes
+ return converted buffer
+********************************************************************/
+static char *
+sj_to_euc (const char *from, BOOL overwrite)
+{
+    register char *out;
+    char *save;
+
+    save = (char *) from;
+    for (out = cvtbuf; *from;) {
+       if (is_shift_jis (*from)) {
+           int code = sjis2euc ((int) from[0] & 0xff, (int) from[1] & 0xff);
+           *out++ = (code >> 8) & 0xff;
+           *out++ = code;
+           from += 2;
+       } else if (is_kana (*from)) {
+           *out++ = euc_kana;
+           *out++ = *from++;
+       } else {
+           *out++ = *from++;
+       }
+    }
+    *out = 0;
+    if (overwrite) {
+       strcpy((char *) save, (char *) cvtbuf);
+       return (char *) save;
+    } else {
+       return cvtbuf;
+    }
+}
+
+/*******************************************************************
+ Convert FROM contain EUC codes to SHIFT JIS codes
+ return converted buffer
+********************************************************************/
+static char *
+euc_to_sj (const char *from, BOOL overwrite)
+{
+    register char *out;
+    char *save;
+
+    save = (char *) from;
+    for (out = cvtbuf; *from; ) {
+       if (is_euc (*from)) {
+           int code = euc2sjis ((int) from[0] & 0xff, (int) from[1] & 0xff);
+           *out++ = (code >> 8) & 0xff;
+           *out++ = code;
+           from += 2;
+       } else if (is_euc_kana (*from)) {
+           *out++ = from[1];
+           from += 2;
+       } else {
+           *out++ = *from++;
+       }
+    }
+    *out = 0;
+    if (overwrite) {
+       strcpy(save, (char *) cvtbuf);
+       return save;
+    } else {
+       return cvtbuf;
+    }
+}
+
+/*******************************************************************
+  JIS7,JIS8,JUNET <-> SJIS
+********************************************************************/
+static int
+sjis2jis (register int hi, register int lo)
+{
+    if (lo >= 0x9f)
+       return ((hi * 2 - (hi >= 0xe0 ? 0x160 : 0xe0)) << 8) | (lo - 0x7e);
+    else
+       return ((hi * 2 - (hi >= 0xe0 ? 0x161 : 0xe1)) << 8) |
+           (lo - (lo >= 0x7f ? 0x20 : 0x1f));
+}
+
+static int
+jis2sjis (register int hi, register int lo)
+{
+    if (hi & 1)
+       return ((hi / 2 + (hi < 0x5f ? 0x71 : 0xb1)) << 8) |
+           (lo + (lo >= 0x60 ? 0x20 : 0x1f));
+    else
+       return ((hi / 2 + (hi < 0x5f ? 0x70 : 0xb0)) << 8) | (lo + 0x7e);
+}
+
+/*******************************************************************
+ Convert FROM contain JIS codes to SHIFT JIS codes
+ return converted buffer
+********************************************************************/
+static char *
+jis8_to_sj (const char *from, BOOL overwrite)
+{
+    register char *out;
+    register int shifted;
+    char *save;
+
+    shifted = _KJ_ROMAN;
+    save = (char *) from;
+    for (out = cvtbuf; *from;) {
+       if (is_esc (*from)) {
+           if (is_so1 (from[1]) && is_so2 (from[2])) {
+               shifted = _KJ_KANJI;
+               from += 3;
+           } else if (is_si1 (from[1]) && is_si2 (from[2])) {
+               shifted = _KJ_ROMAN;
+               from += 3;
+           } else {                    /* sequence error */
+               goto normal;
+           }
+       } else {
+       normal:
+           switch (shifted) {
+           default:
+           case _KJ_ROMAN:
+               *out++ = *from++;
+               break;
+           case _KJ_KANJI:
+               {
+                   int code = jis2sjis ((int) from[0] & 0xff, (int) from[1] & 0xff);
+                   *out++ = (code >> 8) & 0xff;
+                   *out++ = code;
+                   from += 2;
+               }
+               break;
+           }
+       }
+    }
+    *out = 0;
+    if (overwrite) {
+       strcpy (save, (char *) cvtbuf);
+       return save;
+    } else {
+       return cvtbuf;
+    }
+}
+
+/*******************************************************************
+ Convert FROM contain SHIFT JIS codes to JIS codes
+ return converted buffer
+********************************************************************/
+static char *
+sj_to_jis8 (const char *from, BOOL overwrite)
+{
+    register char *out;
+    register int shifted;
+    char *save;
+
+    shifted = _KJ_ROMAN;
+    save = (char *) from;
+    for (out = cvtbuf; *from; ) {
+       if (is_shift_jis (*from)) {
+           int code;
+           switch (shifted) {
+           case _KJ_ROMAN:             /* to KANJI */
+               *out++ = jis_esc;
+               *out++ = jis_so1;
+               *out++ = jis_kso;
+               shifted = _KJ_KANJI;
+               break;
+           }
+           code = sjis2jis ((int) from[0] & 0xff, (int) from[1] & 0xff);
+           *out++ = (code >> 8) & 0xff;
+           *out++ = code;
+           from += 2;
+       } else {
+           switch (shifted) {
+           case _KJ_KANJI:             /* to ROMAN/KANA */
+               *out++ = jis_esc;
+               *out++ = jis_si1;
+               *out++ = jis_ksi;
+               shifted = _KJ_ROMAN;
+               break;
+           }
+           *out++ = *from++;
+       }
+    }
+    switch (shifted) {
+    case _KJ_KANJI:                    /* to ROMAN/KANA */
+       *out++ = jis_esc;
+       *out++ = jis_si1;
+       *out++ = jis_ksi;
+       shifted = _KJ_ROMAN;
+       break;
+    }
+    *out = 0;
+    if (overwrite) {
+       strcpy (save, (char *) cvtbuf);
+       return save;
+    } else {
+       return cvtbuf;
+    }
+}
+
+/*******************************************************************
+ Convert FROM contain 7 bits JIS codes to SHIFT JIS codes
+ return converted buffer
+********************************************************************/
+static char *
+jis7_to_sj (const char *from, BOOL overwrite)
+{
+    register char *out;
+    register int shifted;
+    char *save;
+
+    shifted = _KJ_ROMAN;
+    save = (char *) from;
+    for (out = cvtbuf; *from;) {
+       if (is_esc (*from)) {
+           if (is_so1 (from[1]) && is_so2 (from[2])) {
+               shifted = _KJ_KANJI;
+               from += 3;
+           } else if (is_si1 (from[1]) && is_si2 (from[2])) {
+               shifted = _KJ_ROMAN;
+               from += 3;
+           } else {                    /* sequence error */
+               goto normal;
+           }
+       } else if (is_so (*from)) {
+           shifted = _KJ_KANA;         /* to KANA */
+           from++;
+       } else if (is_si (*from)) {
+           shifted = _KJ_ROMAN;        /* to ROMAN */
+           from++;
+       } else {
+       normal:
+           switch (shifted) {
+           default:
+           case _KJ_ROMAN:
+               *out++ = *from++;
+               break;
+           case _KJ_KANJI:
+               {
+                   int code = jis2sjis ((int) from[0] & 0xff, (int) from[1] & 0xff);
+                   *out++ = (code >> 8) & 0xff;
+                   *out++ = code;
+                   from += 2;
+               }
+               break;
+           case _KJ_KANA:
+               *out++ = ((int) from[0]) + 0x80;
+               break;
+           }
+       }
+    }
+    *out = 0;
+    if (overwrite) {
+       strcpy (save, (char *) cvtbuf);
+       return save;
+    } else {
+       return cvtbuf;
+    }
+}
+
+/*******************************************************************
+ Convert FROM contain SHIFT JIS codes to 7 bits JIS codes
+ return converted buffer
+********************************************************************/
+static char *
+sj_to_jis7 (const char *from, BOOL overwrite)
+{
+    register char *out;
+    register int shifted;
+    char *save;
+
+    shifted = _KJ_ROMAN;
+    save = (char *) from;
+    for (out = cvtbuf; *from; ) {
+       if (is_shift_jis (*from)) {
+           int code;
+           switch (shifted) {
+           case _KJ_KANA:
+               *out++ = jis_si;        /* to ROMAN and through down */
+           case _KJ_ROMAN:             /* to KANJI */
+               *out++ = jis_esc;
+               *out++ = jis_so1;
+               *out++ = jis_kso;
+               shifted = _KJ_KANJI;
+               break;
+           }
+           code = sjis2jis ((int) from[0] & 0xff, (int) from[1] & 0xff);
+           *out++ = (code >> 8) & 0xff;
+           *out++ = code;
+           from += 2;
+       } else if (is_kana (from[0])) {
+           switch (shifted) {
+           case _KJ_KANJI:             /* to ROMAN */
+               *out++ = jis_esc;
+               *out++ = jis_si1;
+               *out++ = jis_ksi;
+           case _KJ_ROMAN:             /* to KANA */
+               *out++ = jis_so;
+               shifted = _KJ_KANA;
+               break;
+           }
+           *out++ = ((int) *from++) - 0x80;
+       } else {
+           switch (shifted) {
+           case _KJ_KANA:
+               *out++ = jis_si;        /* to ROMAN */
+               shifted = _KJ_ROMAN;
+               break;
+           case _KJ_KANJI:             /* to ROMAN */
+               *out++ = jis_esc;
+               *out++ = jis_si1;
+               *out++ = jis_ksi;
+               shifted = _KJ_ROMAN;
+               break;
+           }
+           *out++ = *from++;
+       }
+    }
+    switch (shifted) {
+    case _KJ_KANA:
+       *out++ = jis_si;                /* to ROMAN */
+       break;
+    case _KJ_KANJI:                    /* to ROMAN */
+       *out++ = jis_esc;
+       *out++ = jis_si1;
+       *out++ = jis_ksi;
+       break;
+    }
+    *out = 0;
+    if (overwrite) {
+       strcpy (save, (char *) cvtbuf);
+       return save;
+    } else {
+       return cvtbuf;
+    }
+}
+
+/*******************************************************************
+ Convert FROM contain 7 bits JIS(junet) codes to SHIFT JIS codes
+ return converted buffer
+********************************************************************/
+static char *
+junet_to_sj (const char *from, BOOL overwrite)
+{
+    register char *out;
+    register int shifted;
+    char *save;
+
+    shifted = _KJ_ROMAN;
+    save = (char *) from;
+    for (out = cvtbuf; *from;) {
+       if (is_esc (*from)) {
+           if (is_so1 (from[1]) && is_so2 (from[2])) {
+               shifted = _KJ_KANJI;
+               from += 3;
+           } else if (is_si1 (from[1]) && is_si2 (from[2])) {
+               shifted = _KJ_ROMAN;
+               from += 3;
+           } else if (is_juk1(from[1]) && is_juk2 (from[2])) {
+               shifted = _KJ_KANA;
+               from += 3;
+           } else {                    /* sequence error */
+               goto normal;
+           }
+       } else {
+       normal:
+           switch (shifted) {
+           default:
+           case _KJ_ROMAN:
+               *out++ = *from++;
+               break;
+           case _KJ_KANJI:
+               {
+                   int code = jis2sjis ((int) from[0] & 0xff, (int) from[1] & 0xff);
+                   *out++ = (code >> 8) & 0xff;
+                   *out++ = code;
+                   from += 2;
+               }
+               break;
+           case _KJ_KANA:
+               *out++ = ((int) from[0]) + 0x80;
+               break;
+           }
+       }
+    }
+    *out = 0;
+    if (overwrite) {
+       strcpy (save, (char *) cvtbuf);
+       return save;
+    } else {
+       return cvtbuf;
+    }
+}
+
+/*******************************************************************
+ Convert FROM contain SHIFT JIS codes to 7 bits JIS(junet) codes
+ return converted buffer
+********************************************************************/
+static char *
+sj_to_junet (const char *from, BOOL overwrite)
+{
+    register char *out;
+    register int shifted;
+    char *save;
+
+    shifted = _KJ_ROMAN;
+    save = (char *) from;
+    for (out = cvtbuf; *from; ) {
+       if (is_shift_jis (*from)) {
+           int code;
+           switch (shifted) {
+           case _KJ_KANA:
+           case _KJ_ROMAN:             /* to KANJI */
+               *out++ = jis_esc;
+               *out++ = jis_so1;
+               *out++ = jis_so2;
+               shifted = _KJ_KANJI;
+               break;
+           }
+           code = sjis2jis ((int) from[0] & 0xff, (int) from[1] & 0xff);
+           *out++ = (code >> 8) & 0xff;
+           *out++ = code;
+           from += 2;
+       } else if (is_kana (from[0])) {
+           switch (shifted) {
+           case _KJ_KANJI:             /* to ROMAN */
+           case _KJ_ROMAN:             /* to KANA */
+               *out++ = jis_esc;
+               *out++ = junet_kana1;
+               *out++ = junet_kana2;
+               shifted = _KJ_KANA;
+               break;
+           }
+           *out++ = ((int) *from++) - 0x80;
+       } else {
+           switch (shifted) {
+           case _KJ_KANA:
+           case _KJ_KANJI:             /* to ROMAN */
+               *out++ = jis_esc;
+               *out++ = jis_si1;
+               *out++ = jis_si2;
+               shifted = _KJ_ROMAN;
+               break;
+           }
+           *out++ = *from++;
+       }
+    }
+    switch (shifted) {
+    case _KJ_KANA:
+    case _KJ_KANJI:                    /* to ROMAN */
+       *out++ = jis_esc;
+       *out++ = jis_si1;
+       *out++ = jis_si2;
+       break;
+    }
+    *out = 0;
+    if (overwrite) {
+       strcpy (save, (char *) cvtbuf);
+       return save;
+    } else {
+       return cvtbuf;
+    }
+}
+
+/*******************************************************************
+  HEX <-> SJIS
+********************************************************************/
+/* ":xx" -> a byte */
+static char *
+hex_to_sj (const char *from, BOOL overwrite)
+{
+    char *sp, *dp;
+    
+    sp = (char *) from;
+    dp = cvtbuf;
+    while (*sp) {
+       if (*sp == hex_tag && isxdigit (sp[1]) && isxdigit (sp[2])) {
+           *dp++ = (hex2bin (sp[1])<<4) | (hex2bin (sp[2]));
+           sp += 3;
+       } else
+           *dp++ = *sp++;
+    }
+    *dp = '\0';
+    if (overwrite) {
+       strcpy ((char *) from, (char *) cvtbuf);
+       return (char *) from;
+    } else {
+       return cvtbuf;
+    }
+}
+/*******************************************************************
+  kanji/kana -> ":xx" 
+********************************************************************/
+static char *
+sj_to_hex (const char *from, BOOL overwrite)
+{
+    unsigned char *sp, *dp;
+    
+    sp = (unsigned char*) from;
+    dp = (unsigned char*) cvtbuf;
+    while (*sp) {
+       if (is_kana(*sp)) {
+           *dp++ = hex_tag;
+           *dp++ = bin2hex (((*sp)>>4)&0x0f);
+           *dp++ = bin2hex ((*sp)&0x0f);
+           sp++;
+       } else if (is_shift_jis (*sp) && is_shift_jis2 (sp[1])) {
+           *dp++ = hex_tag;
+           *dp++ = bin2hex (((*sp)>>4)&0x0f);
+           *dp++ = bin2hex ((*sp)&0x0f);
+           sp++;
+           *dp++ = hex_tag;
+           *dp++ = bin2hex (((*sp)>>4)&0x0f);
+           *dp++ = bin2hex ((*sp)&0x0f);
+           sp++;
+       } else
+           *dp++ = *sp++;
+    }
+    *dp = '\0';
+    if (overwrite) {
+       strcpy ((char *) from, (char *) cvtbuf);
+       return (char *) from;
+    } else {
+       return cvtbuf;
+    }
+}
+
+/*******************************************************************
+  kanji/kana -> ":xx" 
+********************************************************************/
+static char *
+sj_to_cap (const char *from, BOOL overwrite)
+{
+    unsigned char *sp, *dp;
+
+    sp = (unsigned char*) from;
+    dp = (unsigned char*) cvtbuf;
+    while (*sp) {
+       if (*sp >= 0x80) {
+           *dp++ = hex_tag;
+           *dp++ = bin2hex (((*sp)>>4)&0x0f);
+           *dp++ = bin2hex ((*sp)&0x0f);
+           sp++;
+       } else {
+           *dp++ = *sp++;
+       }
+    }
+    *dp = '\0';
+    if (overwrite) {
+       strcpy ((char *) from, (char *) cvtbuf);
+       return (char *) from;
+    } else {
+       return cvtbuf;
+    }
+}
+
+/*******************************************************************
+ sj to sj
+********************************************************************/
+static char *
+sj_to_sj (const char *from, BOOL overwrite)
+{
+    if (!overwrite) {
+       strcpy (cvtbuf, (char *) from);
+       return cvtbuf;
+    } else {
+       return (char *) from;
+    }
+}
+
+/************************************************************************
+ conversion:
+ _dos_to_unix          _unix_to_dos
+************************************************************************/
+
+char* (*_dos_to_unix) (const char *str, BOOL overwrite) = sj_to_sj;
+char* (*_unix_to_dos) (const char *str, BOOL overwrite) = sj_to_sj;
+
+static int
+setup_string_function (int codes)
+{
+    switch (codes) {
+    default:
+    case SJIS_CODE:
+       _dos_to_unix = sj_to_sj;
+       _unix_to_dos = sj_to_sj;
+
+       break;
+       
+    case EUC_CODE:
+       _dos_to_unix = sj_to_euc;
+       _unix_to_dos = euc_to_sj;
+       break;
+       
+    case JIS7_CODE:
+       _dos_to_unix = sj_to_jis7;
+       _unix_to_dos = jis7_to_sj;
+       break;
+
+    case JIS8_CODE:
+       _dos_to_unix = sj_to_jis8;
+       _unix_to_dos = jis8_to_sj;
+       break;
+
+    case JUNET_CODE:
+       _dos_to_unix = sj_to_junet;
+       _unix_to_dos = junet_to_sj;
+       break;
+
+    case HEX_CODE:
+       _dos_to_unix = sj_to_hex;
+       _unix_to_dos = hex_to_sj;
+       break;
+
+    case CAP_CODE:
+       _dos_to_unix = sj_to_cap;
+       _unix_to_dos = hex_to_sj;
+       break;
+    }
+    return codes;
+}
+
+/*
+ * Interpret coding system.
+ */
+int 
+interpret_coding_system (char *str, int def)
+{
+    int codes = def;
+    
+    if (strequal (str, "sjis")) {
+       codes = SJIS_CODE;
+    } else if (strequal (str, "euc")) {
+       codes = EUC_CODE;
+    } else if (strequal (str, "cap")) {
+       codes = CAP_CODE;
+       hex_tag = HEXTAG;
+    } else if (strequal (str, "hex")) {
+       codes = HEX_CODE;
+       hex_tag = HEXTAG;
+    } else if (strncasecmp (str, "hex", 3)) {
+       codes = HEX_CODE;
+       hex_tag = (str[3] ? str[3] : HEXTAG);
+    } else if (strequal (str, "j8bb")) {
+       codes = JIS8_CODE;
+       jis_kso = 'B';
+       jis_ksi = 'B';
+    } else if (strequal (str, "j8bj") || strequal (str, "jis8")) {
+       codes = JIS8_CODE;
+       jis_kso = 'B';
+       jis_ksi = 'J';
+    } else if (strequal (str, "j8bh")) {
+       codes = JIS8_CODE;
+       jis_kso = 'B';
+       jis_ksi = 'H';
+    } else if (strequal (str, "j8@b")) {
+       codes = JIS8_CODE;
+       jis_kso = '@';
+       jis_ksi = 'B';
+    } else if (strequal (str, "j8@j")) {
+       codes = JIS8_CODE;
+       jis_kso = '@';
+       jis_ksi = 'J';
+    } else if (strequal (str, "j8@h")) {
+       codes = JIS8_CODE;
+       jis_kso = '@';
+       jis_ksi = 'H';
+    } else if (strequal (str, "j7bb")) {
+       codes = JIS7_CODE;
+       jis_kso = 'B';
+       jis_ksi = 'B';
+    } else if (strequal (str, "j7bj") || strequal (str, "jis7")) {
+       codes = JIS7_CODE;
+       jis_kso = 'B';
+       jis_ksi = 'J';
+    } else if (strequal (str, "j7bh")) {
+       codes = JIS7_CODE;
+       jis_kso = 'B';
+       jis_ksi = 'H';
+    } else if (strequal (str, "j7@b")) {
+       codes = JIS7_CODE;
+       jis_kso = '@';
+       jis_ksi = 'B';
+    } else if (strequal (str, "j7@j")) {
+       codes = JIS7_CODE;
+       jis_kso = '@';
+       jis_ksi = 'J';
+    } else if (strequal (str, "j7@h")) {
+       codes = JIS7_CODE;
+       jis_kso = '@';
+       jis_ksi = 'H';
+    } else if (strequal (str, "jubb")) {
+       codes = JUNET_CODE;
+       jis_kso = 'B';
+       jis_ksi = 'B';
+    } else if (strequal (str, "jubj") || strequal (str, "junet")) {
+       codes = JUNET_CODE;
+       jis_kso = 'B';
+       jis_ksi = 'J';
+    } else if (strequal (str, "jubh")) {
+       codes = JUNET_CODE;
+       jis_kso = 'B';
+       jis_ksi = 'H';
+    } else if (strequal (str, "ju@b")) {
+       codes = JUNET_CODE;
+       jis_kso = '@';
+       jis_ksi = 'B';
+    } else if (strequal (str, "ju@j")) {
+       codes = JUNET_CODE;
+       jis_kso = '@';
+       jis_ksi = 'J';
+    } else if (strequal (str, "ju@h")) {
+       codes = JUNET_CODE;
+       jis_kso = '@';
+       jis_ksi = 'H';
+    }  
+    return setup_string_function (codes);
+}
+#else 
+int kanji_dummy_procedure(void)
+{return 0;}
+#endif /* KANJI */
diff --git a/source/lib/md4.c b/source/lib/md4.c
new file mode 100644 (file)
index 0000000..485e231
--- /dev/null
@@ -0,0 +1,299 @@
+#ifdef SMB_PASSWD
+/*
+   This code is from rfc1186. 
+*/
+
+ /*
+ ** ********************************************************************
+ ** md4.c -- Implementation of MD4 Message Digest Algorithm           **
+ ** Updated: 2/16/90 by Ronald L. Rivest                              **
+ ** (C) 1990 RSA Data Security, Inc.                                  **
+ ** ********************************************************************
+ */
+
+ /*
+ ** To use MD4:
+ **   -- Include md4.h in your program
+ **   -- Declare an MDstruct MD to hold the state of the digest
+ **          computation.
+ **   -- Initialize MD using MDbegin(&MD)
+ **   -- For each full block (64 bytes) X you wish to process, call
+ **          MDupdate(&MD,X,512)
+ **      (512 is the number of bits in a full block.)
+ **   -- For the last block (less than 64 bytes) you wish to process,
+ **          MDupdate(&MD,X,n)
+ **      where n is the number of bits in the partial block. A partial
+ **      block terminates the computation, so every MD computation
+ **      should terminate by processing a partial block, even if it
+ **      has n = 0.
+ **   -- The message digest is available in MD.buffer[0] ...
+ **      MD.buffer[3].  (Least-significant byte of each word
+ **      should be output first.)
+ **   -- You can print out the digest using MDprint(&MD)
+ */
+
+ /* Implementation notes:
+ ** This implementation assumes that ints are 32-bit quantities.
+ ** If the machine stores the least-significant byte of an int in the
+ ** least-addressed byte (e.g., VAX and 8086), then LOWBYTEFIRST
+ ** should be set to TRUE.  Otherwise (e.g., SUNS), LOWBYTEFIRST
+ ** should be set to FALSE.  Note that on machines with LOWBYTEFIRST
+ ** FALSE the routine MDupdate modifies has a side-effect on its input
+ ** array (the order of bytes in each word are reversed).  If this is
+ ** undesired a call to MDreverse(X) can reverse the bytes of X back
+ ** into order after each call to MDupdate.
+ */
+
+#define TRUE  1
+#define FALSE 0
+
+ /* Compile-time includes
+ */
+
+#include <stdio.h>
+#include "md4.h"
+
+#define uchar unsigned char
+#define int16 unsigned short
+#define uint32 unsigned int
+#include "byteorder.h"
+
+ /* Compile-time declarations of MD4 "magic constants".
+ */
+#define I0  0x67452301       /* Initial values for MD buffer */
+#define I1  0xefcdab89
+#define I2  0x98badcfe
+#define I3  0x10325476
+#define C2  013240474631     /* round 2 constant = sqrt(2) in octal */
+#define C3  015666365641     /* round 3 constant = sqrt(3) in octal */
+ /* C2 and C3 are from Knuth, The Art of Programming, Volume 2
+ ** (Seminumerical Algorithms), Second Edition (1981), Addison-Wesley.
+ ** Table 2, page 660.
+ */
+
+#define fs1  3               /* round 1 shift amounts */
+#define fs2  7
+#define fs3 11
+#define fs4 19
+#define gs1  3               /* round 2 shift amounts */
+#define gs2  5
+#define gs3  9
+#define gs4 13
+#define hs1  3               /* round 3 shift amounts */
+#define hs2  9
+#define hs3 11
+#define hs4 15
+
+ /* Compile-time macro declarations for MD4.
+ ** Note: The "rot" operator uses the variable "tmp".
+ ** It assumes tmp is declared as unsigned int, so that the >>
+ ** operator will shift in zeros rather than extending the sign bit.
+ */
+#define f(X,Y,Z)             ((X&Y) | ((~X)&Z))
+#define g(X,Y,Z)             ((X&Y) | (X&Z) | (Y&Z))
+#define h(X,Y,Z)             (X^Y^Z)
+#define rot(X,S)             (tmp=X,(tmp<<S) | (tmp>>(32-S)))
+#define ff(A,B,C,D,i,s)      A = rot((A + f(B,C,D) + X[i]),s)
+#define gg(A,B,C,D,i,s)      A = rot((A + g(B,C,D) + X[i] + C2),s)
+#define hh(A,B,C,D,i,s)      A = rot((A + h(B,C,D) + X[i] + C3),s)
+
+ /* MDprint(MDp)
+ ** Print message digest buffer MDp as 32 hexadecimal digits.
+ ** Order is from low-order byte of buffer[0] to high-order byte of
+ ** buffer[3].
+ ** Each byte is printed with high-order hexadecimal digit first.
+ ** This is a user-callable routine.
+ */
+ void
+ MDprint(MDp)
+ MDptr MDp;
+ { int i,j;
+   for (i=0;i<4;i++)
+     for (j=0;j<32;j=j+8)
+       printf("%02x",(MDp->buffer[i]>>j) & 0xFF);
+ }
+
+ /* MDbegin(MDp)
+ ** Initialize message digest buffer MDp.
+ ** This is a user-callable routine.
+ */
+ void
+ MDbegin(MDp)
+ MDptr MDp;
+ { int i;
+   MDp->buffer[0] = I0;
+   MDp->buffer[1] = I1;
+   MDp->buffer[2] = I2;
+   MDp->buffer[3] = I3;
+   for (i=0;i<8;i++) MDp->count[i] = 0;
+   MDp->done = 0;
+ }
+
+ /* MDreverse(X)
+ ** Reverse the byte-ordering of every int in X.
+ ** Assumes X is an array of 16 ints.
+ ** The macro revx reverses the byte-ordering of the next word of X.
+ */
+ void MDreverse(X)
+ unsigned int *X;
+ { register unsigned int t;
+   register unsigned int i;
+
+   for(i = 0; i < 16; i++) {
+         t = X[i];
+         SIVAL(X,i*4,t);
+       }
+ }
+
+ /* MDblock(MDp,X)
+ ** Update message digest buffer MDp->buffer using 16-word data block X.
+ ** Assumes all 16 words of X are full of data.
+ ** Does not update MDp->count.
+ ** This routine is not user-callable.
+ */
+ static void
+ MDblock(MDp,X)
+ MDptr MDp;
+ unsigned int *X;
+ {
+   register unsigned int tmp, A, B, C, D;
+   MDreverse(X);
+   A = MDp->buffer[0];
+   B = MDp->buffer[1];
+   C = MDp->buffer[2];
+   D = MDp->buffer[3];
+   /* Update the message digest buffer */
+   ff(A , B , C , D ,  0 , fs1); /* Round 1 */
+   ff(D , A , B , C ,  1 , fs2);
+   ff(C , D , A , B ,  2 , fs3);
+   ff(B , C , D , A ,  3 , fs4);
+   ff(A , B , C , D ,  4 , fs1);
+   ff(D , A , B , C ,  5 , fs2);
+   ff(C , D , A , B ,  6 , fs3);
+   ff(B , C , D , A ,  7 , fs4);
+   ff(A , B , C , D ,  8 , fs1);
+   ff(D , A , B , C ,  9 , fs2);
+   ff(C , D , A , B , 10 , fs3);
+   ff(B , C , D , A , 11 , fs4);
+   ff(A , B , C , D , 12 , fs1);
+   ff(D , A , B , C , 13 , fs2);
+   ff(C , D , A , B , 14 , fs3);
+   ff(B , C , D , A , 15 , fs4);
+   gg(A , B , C , D ,  0 , gs1); /* Round 2 */
+   gg(D , A , B , C ,  4 , gs2);
+   gg(C , D , A , B ,  8 , gs3);
+   gg(B , C , D , A , 12 , gs4);
+   gg(A , B , C , D ,  1 , gs1);
+   gg(D , A , B , C ,  5 , gs2);
+   gg(C , D , A , B ,  9 , gs3);
+   gg(B , C , D , A , 13 , gs4);
+   gg(A , B , C , D ,  2 , gs1);
+   gg(D , A , B , C ,  6 , gs2);
+   gg(C , D , A , B , 10 , gs3);
+   gg(B , C , D , A , 14 , gs4);
+   gg(A , B , C , D ,  3 , gs1);
+   gg(D , A , B , C ,  7 , gs2);
+   gg(C , D , A , B , 11 , gs3);
+   gg(B , C , D , A , 15 , gs4);
+   hh(A , B , C , D ,  0 , hs1); /* Round 3 */
+   hh(D , A , B , C ,  8 , hs2);
+   hh(C , D , A , B ,  4 , hs3);
+   hh(B , C , D , A , 12 , hs4);
+   hh(A , B , C , D ,  2 , hs1);
+   hh(D , A , B , C , 10 , hs2);
+   hh(C , D , A , B ,  6 , hs3);
+   hh(B , C , D , A , 14 , hs4);
+   hh(A , B , C , D ,  1 , hs1);
+   hh(D , A , B , C ,  9 , hs2);
+   hh(C , D , A , B ,  5 , hs3);
+   hh(B , C , D , A , 13 , hs4);
+   hh(A , B , C , D ,  3 , hs1);
+   hh(D , A , B , C , 11 , hs2);
+   hh(C , D , A , B ,  7 , hs3);
+   hh(B , C , D , A , 15 , hs4);
+   MDp->buffer[0] += A;
+   MDp->buffer[1] += B;
+   MDp->buffer[2] += C;
+   MDp->buffer[3] += D;
+ }
+
+ /* MDupdate(MDp,X,count)
+ ** Input: MDp -- an MDptr
+ **        X -- a pointer to an array of unsigned characters.
+ **        count -- the number of bits of X to use.
+ **          (if not a multiple of 8, uses high bits of last byte.)
+ ** Update MDp using the number of bits of X given by count.
+ ** This is the basic input routine for an MD4 user.
+ ** The routine completes the MD computation when count < 512, so
+ ** every MD computation should end with one call to MDupdate with a
+ ** count less than 512.  A call with count 0 will be ignored if the
+ ** MD has already been terminated (done != 0), so an extra call with
+ ** count 0 can be given as a "courtesy close" to force termination
+ ** if desired.
+ */
+ void
+ MDupdate(MDp,X,count)
+ MDptr MDp;
+ unsigned char *X;
+ unsigned int count;
+ { unsigned int i, tmp, bit, byte, mask;
+   unsigned char XX[64];
+   unsigned char *p;
+   /* return with no error if this is a courtesy close with count
+   ** zero and MDp->done is true.
+   */
+   if (count == 0 && MDp->done) return;
+   /* check to see if MD is already done and report error */
+   if (MDp->done)
+          { printf("\nError: MDupdate MD already done."); return; }
+   /* Add count to MDp->count */
+   tmp = count;
+   p = MDp->count;
+   while (tmp)
+     { tmp += *p;
+       *p++ = tmp;
+       tmp = tmp >> 8;
+     }
+   /* Process data */
+   if (count == 512)
+     { /* Full block of data to handle */
+       MDblock(MDp,(unsigned int *)X);
+     }
+   else if (count > 512) /* Check for count too large */
+     { printf("\nError: MDupdate called with illegal count value %d."
+              ,count);
+       return;
+     }
+   else /* partial block -- must be last block so finish up */
+     { /* Find out how many bytes and residual bits there are */
+       byte = count >> 3;
+       bit =  count & 7;
+       /* Copy X into XX since we need to modify it */
+       for (i=0;i<=byte;i++)   XX[i] = X[i];
+       for (i=byte+1;i<64;i++) XX[i] = 0;
+       /* Add padding '1' bit and low-order zeros in last byte */
+       mask = 1 << (7 - bit);
+       XX[byte] = (XX[byte] | mask) & ~( mask - 1);
+       /* If room for bit count, finish up with this block */
+       if (byte <= 55)
+         { for (i=0;i<8;i++) XX[56+i] = MDp->count[i];
+           MDblock(MDp,(unsigned int *)XX);
+         }
+       else /* need to do two blocks to finish up */
+         { MDblock(MDp,(unsigned int *)XX);
+           for (i=0;i<56;i++) XX[i] = 0;
+           for (i=0;i<8;i++)  XX[56+i] = MDp->count[i];
+           MDblock(MDp,(unsigned int *)XX);
+         }
+       /* Set flag saying we're done with MD computation */
+       MDp->done = 1;
+     }
+ }
+
+ /*
+ ** End of md4.c
+ */
+#else
+void md4_dummy() {;}
+#endif
diff --git a/source/lib/system.c b/source/lib/system.c
new file mode 100644 (file)
index 0000000..938746e
--- /dev/null
@@ -0,0 +1,222 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   Samba system utilities
+   Copyright (C) Andrew Tridgell 1992-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"
+
+extern int DEBUGLEVEL;
+
+/*
+   The idea is that this file will eventually have wrappers around all
+   important system calls in samba. The aim is twofold:
+
+   - 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
+*/
+
+
+/*******************************************************************
+this replaces the normal select() system call
+return if some data has arrived on one of the file descriptors
+return -1 means error
+********************************************************************/
+#ifdef NO_SELECT
+static int pollfd(int fd)
+{
+  int     r=0;
+
+#ifdef HAS_RDCHK
+  r = rdchk(fd);
+#elif defined(TCRDCHK)
+  (void)ioctl(fd, TCRDCHK, &r);
+#else
+  (void)ioctl(fd, FIONREAD, &r);
+#endif
+
+  return(r);
+}
+
+int sys_select(fd_set *fds,struct timeval *tval)
+{
+  fd_set fds2;
+  int counter=0;
+  int found=0;
+
+  FD_ZERO(&fds2);
+
+  while (1) 
+    {
+      int i;
+      for (i=0;i<255;i++) {
+       if (FD_ISSET(i,fds) && pollfd(i)>0) {
+         found++;
+         FD_SET(i,&fds2);
+       }
+      }
+
+      if (found) {
+       memcpy((void *)fds,(void *)&fds2,sizeof(fds2));
+       return(found);
+      }
+      
+      if (tval && tval.tv_sec < counter) return(0);
+      sleep(1);
+      counter++;
+    }
+}
+
+#else
+int sys_select(fd_set *fds,struct timeval *tval)
+{
+  struct timeval t2;
+  int selrtn;
+
+  do {
+    if (tval) memcpy((void *)&t2,(void *)tval,sizeof(t2));
+    errno = 0;
+    selrtn = select(16,SELECT_CAST fds,NULL,NULL,tval?&t2:NULL);
+  } while (selrtn<0 && errno == EINTR);
+
+  return(selrtn);
+}
+#endif
+
+
+/*******************************************************************
+just a unlink wrapper
+********************************************************************/
+int sys_unlink(char *fname)
+{
+  return(unlink(dos_to_unix(fname,False)));
+}
+
+
+/*******************************************************************
+a simple open() wrapper
+********************************************************************/
+int sys_open(char *fname,int flags,int mode)
+{
+  return(open(dos_to_unix(fname,False),flags,mode));
+}
+
+
+/*******************************************************************
+a simple opendir() wrapper
+********************************************************************/
+DIR *sys_opendir(char *dname)
+{
+  return(opendir(dos_to_unix(dname,False)));
+}
+
+
+/*******************************************************************
+and a stat() wrapper
+********************************************************************/
+int sys_stat(char *fname,struct stat *sbuf)
+{
+  return(stat(dos_to_unix(fname,False),sbuf));
+}
+
+/*******************************************************************
+don't forget lstat()
+********************************************************************/
+int sys_lstat(char *fname,struct stat *sbuf)
+{
+  return(lstat(dos_to_unix(fname,False),sbuf));
+}
+
+
+/*******************************************************************
+mkdir() gets a wrapper
+********************************************************************/
+int sys_mkdir(char *dname,int mode)
+{
+  return(mkdir(dos_to_unix(dname,False),mode));
+}
+
+
+/*******************************************************************
+do does rmdir()
+********************************************************************/
+int sys_rmdir(char *dname)
+{
+  return(rmdir(dos_to_unix(dname,False)));
+}
+
+
+/*******************************************************************
+I almost forgot chdir()
+********************************************************************/
+int sys_chdir(char *dname)
+{
+  return(chdir(dos_to_unix(dname,False)));
+}
+
+
+/*******************************************************************
+now for utime()
+********************************************************************/
+int sys_utime(char *fname,struct utimbuf *times)
+{
+  return(utime(dos_to_unix(fname,False),times));
+}
+
+/*******************************************************************
+for rename()
+********************************************************************/
+int sys_rename(char *from, char *to)
+{
+#ifdef KANJI
+    pstring zfrom, zto;
+    strcpy (zfrom, dos_to_unix (from, False));
+    strcpy (zto, dos_to_unix (to, False));
+    return rename (zfrom, zto);
+#else 
+    return rename (from, to);
+#endif /* KANJI */
+}
+
+
+/*******************************************************************
+chown isn't used much but OS/2 doesn't have it
+********************************************************************/
+int sys_chown(char *fname,int uid,int gid)
+{
+#ifdef NO_CHOWN
+  DEBUG(1,("Warning - chown(%s,%d,%d) not done\n",fname,uid,gid));
+#else
+  return(chown(fname,uid,gid));
+#endif
+}
+
+/*******************************************************************
+os/2 also doesn't have chroot
+********************************************************************/
+int sys_chroot(char *dname)
+{
+#ifdef NO_CHROOT
+  DEBUG(1,("Warning - chroot(%s) not done\n",dname));
+#else
+  return(chroot(dname));
+#endif
+}
diff --git a/source/lib/ufc.c b/source/lib/ufc.c
new file mode 100644 (file)
index 0000000..8417285
--- /dev/null
@@ -0,0 +1,782 @@
+/*
+   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.
+
+*/
+
+#ifdef UFC_CRYPT
+
+/*
+ * UFC-crypt: ultra fast crypt(3) implementation
+ *
+ * Copyright (C) 1991, 1992, 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
+ *
+ */
+#include "includes.h"
+
+
+#ifndef long32
+#define long32 int32
+#endif
+
+#ifndef long64
+#define long64 int64
+#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(start, cnt)
+  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()
+  { 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 mask1,comes_from;
+       
+      comes_from = perm32[esel[bit]-1]-1;
+      mask1      = bytemask[comes_from % 8];
+       
+      for(j = 256; j--;) {
+       if(j & 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, mask1, 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  */
+
+      mask1 = longmask[bit_within_word + 26];
+      mask2 = longmask[o_bit];
+
+      for(word_value = 64; word_value--;) {
+       if(word_value & mask1)
+         efp[comes_from_word][word_value][o_long] |= 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(k, saltbits)
+  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(k, saltbits)
+  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(char *s1)
+  { ufc_long i, j, saltbits;
+    unsigned char *s2 = (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(key)
+  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(l1, l2, r1, r2)
+  ufc_long l1,l2,r1,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(v1, v2, salt)
+  ufc_long v1, v2;
+  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;
+  }
+
+ufc_long *_ufc_doit();
+
+/* 
+ * UNIX crypt function
+ */
+   
+char *ufc_crypt(char *key,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)))
+
+ufc_long *_ufc_doit(l1, l2, r1, r2, itr)
+  ufc_long l1, l2, r1, r2, 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)))
+
+ufc_long *_ufc_doit(l1, l2, r1, r2, itr)
+  ufc_long l1, l2, r1, r2, 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
+
+
+#else
+int ufc_dummy_procedure(void)
+{return 0;}
+#endif
diff --git a/source/lib/username.c b/source/lib/username.c
new file mode 100644 (file)
index 0000000..3d214fb
--- /dev/null
@@ -0,0 +1,246 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   Username handling
+   Copyright (C) Andrew Tridgell 1992-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"
+#include "loadparm.h"
+extern int DEBUGLEVEL;
+
+
+/****************************************************************************
+get a users home directory. tries as-is then lower case
+****************************************************************************/
+char *get_home_dir(char *user)
+{
+  static struct passwd *pass;
+
+  pass = Get_Pwnam(user,False);
+
+  if (!pass) return(NULL);
+  return(pass->pw_dir);      
+}
+
+
+/*******************************************************************
+map a username from a dos name to a unix name by looking in the username
+map
+********************************************************************/
+void map_username(char *user)
+{
+  static int depth=0;
+  static BOOL initialised=False;
+  static fstring last_from,last_to;
+  FILE *f;
+  char *s;
+  char *mapfile = lp_username_map();
+  if (!*mapfile || depth) return;
+
+  if (!*user) return;
+
+  if (!initialised) {
+    *last_from = *last_to = 0;
+    initialised = True;
+  }
+
+  if (strequal(user,last_to)) return;
+
+  if (strequal(user,last_from)) {
+    DEBUG(3,("Mapped user %s to %s\n",user,last_to));
+    strcpy(user,last_to);
+    return;
+  }
+  
+  f = fopen(mapfile,"r");
+  if (!f) {
+    DEBUG(0,("can't open username map %s\n",mapfile));
+    return;
+  }
+
+  DEBUG(4,("Scanning username map %s\n",mapfile));
+
+  depth++;
+
+  for (; (s=fgets_slash(NULL,80,f)); free(s)) {
+    char *unixname = s;
+    char *dosname = strchr(unixname,'=');
+
+    if (!dosname) continue;
+    *dosname++ = 0;
+
+    while (isspace(*unixname)) unixname++;
+    if (!*unixname || strchr("#;",*unixname)) continue;
+
+    {
+      int l = strlen(unixname);
+      while (l && isspace(unixname[l-1])) {
+       unixname[l-1] = 0;
+       l--;
+      }
+    }
+
+    if (strchr(dosname,'*') || user_in_list(user,dosname)) {
+      DEBUG(3,("Mapped user %s to %s\n",user,unixname));
+      StrnCpy(last_from,user,sizeof(last_from)-1);
+      sscanf(unixname,"%s",user);
+      StrnCpy(last_to,user,sizeof(last_to)-1);
+    }
+  }
+
+  fclose(f);
+
+  depth--;
+}
+
+/****************************************************************************
+internals of Get_Pwnam wrapper
+****************************************************************************/
+static struct passwd *_Get_Pwnam(char *s)
+{
+  struct passwd *ret;
+
+  ret = getpwnam(s);
+  if (ret)
+    {
+#ifdef GETPWANAM
+      struct passwd_adjunct *pwret;
+      pwret = getpwanam(s);
+      if (pwret)
+       {
+         free(ret->pw_passwd);
+         ret->pw_passwd = pwret->pwa_passwd;
+       }
+#endif
+
+    }
+
+  return(ret);
+}
+
+
+/****************************************************************************
+a wrapper for getpwnam() that tries with all lower and all upper case 
+if the initial name fails. Also tried with first letter capitalised
+Note that this changes user!
+****************************************************************************/
+struct passwd *Get_Pwnam(char *user,BOOL allow_change)
+{
+  fstring user2;
+
+  struct passwd *ret;  
+
+  if (!user || !(*user))
+    return(NULL);
+
+  StrnCpy(user2,user,sizeof(user2)-1);
+
+  if (!allow_change) {
+    user = &user2[0];
+  }
+
+  map_username(user);
+
+  ret = _Get_Pwnam(user);
+  if (ret) return(ret);
+
+  strlower(user);
+  ret = _Get_Pwnam(user);
+  if (ret)  return(ret);
+
+  strupper(user);
+  ret = _Get_Pwnam(user);
+  if (ret) return(ret);
+
+  /* try with first letter capitalised */
+  if (strlen(user) > 1)
+    strlower(user+1);  
+  ret = _Get_Pwnam(user);
+  if (ret) return(ret);
+
+  if (allow_change)
+    strcpy(user,user2);
+
+  return(NULL);
+}
+
+
+/****************************************************************************
+check if a user is in a user list
+****************************************************************************/
+BOOL user_in_list(char *user,char *list)
+{
+  pstring tok;
+  char *p=list;
+
+  while (next_token(&p,tok,LIST_SEP))
+    {
+      if (strequal(user,tok))
+       return(True);
+
+#ifdef NETGROUP
+      if (*tok == '@')
+       {
+         static char *mydomain = NULL;
+         if (mydomain == 0)
+           yp_get_default_domain(&mydomain);
+         
+         DEBUG(5,("looking for user %s of domain %s in netgroup %s\n",
+                  user, mydomain, &tok[1]));
+         DEBUG(5,("innetgr is %s\n",
+                  innetgr(&tok[1], (char *) 0, user, mydomain)
+                  ? "TRUE" : "FALSE"));
+         
+         if (innetgr(&tok[1], (char *)0, user, mydomain))
+           return (True);
+       }
+#endif
+
+
+#if HAVE_GETGRNAM 
+      if (*tok == '@')
+       {
+          struct group *gptr;
+          char **member;  
+         struct passwd *pass = Get_Pwnam(user,False);
+
+         if (pass) { 
+           gptr = getgrgid(pass->pw_gid);
+           if (gptr && strequal(gptr->gr_name,&tok[1]))
+             return(True); 
+         } 
+
+         gptr = (struct group *)getgrnam(&tok[1]);
+
+         if (gptr)
+           {
+             member = gptr->gr_mem;
+             while (member && *member)
+               {
+                 if (strequal(*member,user))
+                   return(True);
+                 member++;
+               }
+           }
+       }             
+#endif
+    }
+  return(False);
+}
+
+
diff --git a/source/lib/util.c b/source/lib/util.c
new file mode 100644 (file)
index 0000000..7bd6298
--- /dev/null
@@ -0,0 +1,4510 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   Samba utility functions
+   Copyright (C) Andrew Tridgell 1992-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"
+#include "loadparm.h"
+
+pstring scope = "";
+
+int DEBUGLEVEL = 1;
+
+BOOL passive = False;
+
+int Protocol = PROTOCOL_COREPLUS;
+
+int serverzone=0;
+
+/* a default finfo structure to ensure all fields are sensible */
+file_info def_finfo = {-1,0,0,0,0,0,0,""};
+
+/* these are some file handles where debug info will be stored */
+FILE *dbf = NULL;
+
+/* the client file descriptor */
+int Client = -1;
+
+/* info on the client */
+struct from_host Client_info=
+{"UNKNOWN","0.0.0.0",NULL};
+
+/* the last IP received from */
+struct in_addr lastip;
+
+/* the last port received from */
+int lastport=0;
+
+/* my IP, the broadcast IP and the Netmask */
+struct in_addr myip;
+struct in_addr bcast_ip;
+struct in_addr Netmask;
+
+int trans_num = 0;
+
+/*
+   case handling on filenames 
+*/
+int case_default = CASE_LOWER;
+
+
+/* size of reads during a direct file to file transfer */
+int ReadSize = 16*1024;
+
+pstring debugf = "/tmp/log.samba";
+int syslog_level;
+
+/* the following control case operations - they are put here so the
+   client can link easily */
+BOOL case_sensitive;
+BOOL case_preserve;
+BOOL use_mangled_map = False;
+BOOL short_case_preserve;
+BOOL case_mangle;
+
+fstring remote_machine="";
+fstring local_machine="";
+fstring remote_arch="UNKNOWN";
+fstring remote_proto="UNKNOWN";
+pstring myhostname="";
+pstring user_socket_options="";   
+pstring sesssetup_user="";
+
+
+static char *filename_dos(char *path,char *buf);
+
+static BOOL stdout_logging = False;
+
+
+/*******************************************************************
+  get ready for syslog stuff
+  ******************************************************************/
+void setup_logging(char *pname,BOOL interactive)
+{
+#ifdef SYSLOG
+  if (!interactive) {
+    char *p = strrchr(pname,'/');
+    if (p) pname = p+1;
+    openlog(pname, LOG_PID, LOG_DAEMON);
+  }
+#endif
+  if (interactive) {
+    stdout_logging = True;
+    dbf = stdout;
+  }
+}
+
+
+BOOL append_log=False;
+
+
+/****************************************************************************
+reopen the log files
+****************************************************************************/
+void reopen_logs(void)
+{
+  extern FILE *dbf;
+  pstring fname;
+  
+  if (DEBUGLEVEL > 0)
+    {
+      strcpy(fname,debugf);
+      if (lp_loaded() && (*lp_logfile()))
+       strcpy(fname,lp_logfile());
+
+      if (!strcsequal(fname,debugf) || !dbf || !file_exist(debugf,NULL))
+       {
+         strcpy(debugf,fname);
+         if (dbf) fclose(dbf);
+         if (append_log)
+           dbf = fopen(debugf,"a");
+         else
+           dbf = fopen(debugf,"w");
+         if (dbf) setbuf(dbf,NULL);
+       }
+    }
+  else
+    {
+      if (dbf)
+       {
+         fclose(dbf);
+         dbf = NULL;
+       }
+    }
+}
+
+
+/*******************************************************************
+write an debug message on the debugfile. This is called by the DEBUG
+macro
+********************************************************************/
+#ifdef __STDC__
+int Debug1(char *format_str, ...)
+{
+#else
+int Debug1(va_alist)
+va_dcl
+{  
+  char *format_str;
+#endif
+  va_list ap;  
+  
+#ifdef __STDC__
+  va_start(ap, format_str);
+#else
+  va_start(ap);
+  format_str = va_arg(ap,char *);
+#endif
+
+  if (stdout_logging) {
+    vfprintf(dbf,format_str,ap);
+    va_end(ap);
+    return(0);
+  }
+
+  {
+    static int debug_count=0;
+
+    debug_count++;
+    if (debug_count == 100) {
+      int maxlog = lp_max_log_size() * 1024;
+      if (dbf && maxlog > 0)
+       {
+         struct stat st;
+
+         if (fstat(fileno(dbf),&st) == 0 && st.st_size > maxlog) {
+           fclose(dbf); dbf = NULL;
+           reopen_logs();
+           if (dbf && file_size(debugf) > maxlog) {
+             pstring name;
+             fclose(dbf); dbf = NULL;
+             sprintf(name,"%s.old",debugf);
+             sys_rename(debugf,name);
+             reopen_logs();
+           }
+         }
+       }
+      debug_count=0;
+    }
+  }
+  
+#ifdef SYSLOG
+  if (!lp_syslog_only())
+#endif  
+    {
+      if (!dbf) 
+       {
+         dbf = fopen(debugf,"w");
+         if (dbf)
+           setbuf(dbf,NULL);
+         else
+           return(0);
+       }
+    }
+
+#ifdef SYSLOG
+  if (syslog_level < lp_syslog())
+    {
+      /* 
+       * map debug levels to syslog() priorities
+       * note that not all DEBUG(0, ...) calls are
+       * necessarily errors
+       */
+      static int priority_map[] = { 
+       LOG_ERR,     /* 0 */
+       LOG_WARNING, /* 1 */
+       LOG_NOTICE,  /* 2 */
+       LOG_INFO,    /* 3 */
+      };
+      int priority;
+      pstring msgbuf;
+      
+      if (syslog_level >= sizeof(priority_map) / sizeof(priority_map[0]) ||
+         syslog_level < 0)
+       priority = LOG_DEBUG;
+      else
+       priority = priority_map[syslog_level];
+      
+      vsprintf(msgbuf, format_str, ap);
+      
+      msgbuf[255] = '\0';
+      syslog(priority, "%s", msgbuf);
+    }
+#endif
+  
+#ifdef SYSLOG
+  if (!lp_syslog_only())
+#endif
+    {
+      vfprintf(dbf,format_str,ap);
+      fflush(dbf);
+    }
+  
+  va_end(ap);
+  return(0);
+}
+
+/****************************************************************************
+routine to do file locking
+****************************************************************************/
+BOOL fcntl_lock(int fd,int op,uint32 offset,uint32 count,int type)
+{
+#if HAVE_FCNTL_LOCK
+  struct flock lock;
+  int ret;
+
+#if 1
+  uint32 mask = 0xC0000000;
+
+  /* make sure the count is reasonable, we might kill the lockd otherwise */
+  count &= ~mask;
+
+  /* the offset is often strange - remove 2 of its bits if either of
+     the top two bits are set. Shift the top ones by two bits. This
+     still allows OLE2 apps to operate, but should stop lockd from
+     dieing */
+  if ((offset & mask) != 0)
+    offset = (offset & ~mask) | ((offset & mask) >> 2);
+#else
+  unsigned long mask = ((unsigned)1<<31);
+
+  /* interpret negative counts as large numbers */
+  if (count < 0)
+    count &= ~mask;
+
+  /* no negative offsets */
+  offset &= ~mask;
+
+  /* count + offset must be in range */
+  while ((offset < 0 || (offset + count < 0)) && mask)
+    {
+      offset &= ~mask;
+      mask = mask >> 1;
+    }
+#endif
+
+
+  DEBUG(5,("fcntl_lock %d %d %d %d %d\n",fd,op,(int)offset,(int)count,type));
+
+  lock.l_type = type;
+  lock.l_whence = SEEK_SET;
+  lock.l_start = (int)offset;
+  lock.l_len = (int)count;
+  lock.l_pid = 0;
+
+  errno = 0;
+
+  ret = fcntl(fd,op,&lock);
+
+  if (errno != 0)
+    DEBUG(3,("fcntl lock gave errno %d (%s)\n",errno,strerror(errno)));
+
+  /* a lock query */
+  if (op == F_GETLK)
+    {
+      if ((ret != -1) &&
+         (lock.l_type != F_UNLCK) && 
+         (lock.l_pid != 0) && 
+         (lock.l_pid != getpid()))
+       {
+         DEBUG(3,("fd %d is locked by pid %d\n",fd,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,("lock failed at offset %d count %d op %d type %d (%s)\n",
+              offset,count,op,type,strerror(errno)));
+
+      /* perhaps it doesn't support this sort of locking?? */
+      if (errno == EINVAL)
+       {
+         DEBUG(3,("locking not supported? returning True\n"));
+         return(True);
+       }
+
+      return(False);
+    }
+
+  /* everything went OK */
+  DEBUG(5,("Lock call successful\n"));
+
+  return(True);
+#else
+  return(False);
+#endif
+}
+
+/*******************************************************************
+lock a file - returning a open file descriptor or -1 on failure
+The timeout is in seconds. 0 means no timeout
+********************************************************************/
+int file_lock(char *name,int timeout)
+{  
+  int fd = open(name,O_RDWR|O_CREAT,0666);
+  time_t t=0;
+  if (fd < 0) return(-1);
+
+#if HAVE_FCNTL_LOCK
+  if (timeout) t = time(NULL);
+  while (!timeout || (time(NULL)-t < timeout)) {
+    if (fcntl_lock(fd,F_SETLK,0,1,F_WRLCK)) return(fd);    
+    msleep(LOCK_RETRY_TIMEOUT);
+  }
+  return(-1);
+#else
+  return(fd);
+#endif
+}
+
+/*******************************************************************
+unlock a file locked by file_lock
+********************************************************************/
+void file_unlock(int fd)
+{
+  if (fd<0) return;
+#if HAVE_FCNTL_LOCK
+  fcntl_lock(fd,F_SETLK,0,1,F_UNLCK);
+#endif
+  close(fd);
+}
+
+/*******************************************************************
+a gettimeofday wrapper
+********************************************************************/
+void GetTimeOfDay(struct timeval *tval)
+{
+#ifdef GETTIMEOFDAY1
+  gettimeofday(tval);
+#else
+  gettimeofday(tval,NULL);
+#endif
+}
+
+int extra_time_offset = 0;
+
+static int timediff = 0;
+
+/*******************************************************************
+init the time differences
+********************************************************************/
+void TimeInit(void)
+{
+  struct tm tm_utc,tm_local;
+  time_t t;
+
+  t = time(NULL);
+
+  tm_utc = *(gmtime(&t));
+  tm_local = *(localtime(&t));
+
+#ifdef HAVE_GMTOFF
+  timediff = -tm_local.tm_gmtoff;  
+#else
+  timediff = mktime(&tm_utc) - mktime(&tm_local);
+#endif
+
+  if (serverzone == 0) {
+    serverzone = timediff - DSTDiff(t);
+    DEBUG(4,("Serverzone is %d\n",serverzone));
+  }
+}
+
+
+/*******************************************************************
+return the DST offset for a particular time
+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.
+********************************************************************/
+int DSTDiff(time_t t)
+{
+  static struct dst_table {time_t start,end; BOOL is_dst;} *dst_table = NULL;
+  static int table_size = 0;
+  int i;
+  BOOL is_dst = False;
+
+  if (t == 0) t = time(NULL);
+
+#ifndef NO_ISDST
+  for (i=0;i<table_size;i++)
+    if (t >= dst_table[i].start && t <= dst_table[i].end) break;
+
+  if (i<table_size) {
+    is_dst = dst_table[i].is_dst;
+  } else {
+    time_t low,high;
+
+    dst_table = (struct dst_table *)Realloc(dst_table,
+                                             sizeof(dst_table[0])*(i+1));
+    if (!dst_table) {
+      table_size = 0;
+      return(0);
+    }
+
+    table_size++;
+
+    dst_table[i].is_dst = is_dst = (localtime(&t)->tm_isdst?True:False);;
+    dst_table[i].start = dst_table[i].end = t;
+    
+    /* no entry will cover more than 6 months */
+    low = t - 3*30*24*60*60;
+    high = t + 3*30*24*60*60;
+
+    /* widen the new entry using two bisection searches */
+    while (low+60*60 < dst_table[i].start) {
+      t = low + (dst_table[i].start-low)/2;
+      if ((localtime(&t)->tm_isdst?True:False) == is_dst)
+       dst_table[i].start = t;
+      else
+       low = t;
+    }
+
+    while (high-60*60 > dst_table[i].end) {
+      t = high + (high-dst_table[i].end)/2;
+      if ((localtime(&t)->tm_isdst?True:False) == is_dst)
+       dst_table[i].end = t;
+      else
+       high = t;
+    }    
+
+/*
+    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].is_dst));
+*/
+  }
+#endif
+
+  return((is_dst?60*60:0) - (extra_time_offset*60));
+}
+
+/****************************************************************************
+return the difference between local and GMT time
+****************************************************************************/
+int TimeDiff(time_t t)
+{
+  static BOOL initialised = False;
+  if (!initialised) {initialised=True; TimeInit();}
+  return(timediff - DSTDiff(t));
+}
+
+/****************************************************************************
+try to optimise the localtime call, it can be quite expenive on some machines
+timemul is normally LOCAL_TO_GMT, GMT_TO_LOCAL or 0
+****************************************************************************/
+struct tm *LocalTime(time_t *t,int timemul)
+{
+  time_t t2 = *t;
+
+  if (timemul)
+    t2 += timemul * TimeDiff(t2);
+
+  return(gmtime(&t2));
+}
+
+
+/****************************************************************************
+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);
+}
+
+
+static char *last_ptr=NULL;
+
+/****************************************************************************
+  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(char **ptr,char *buff,char *sep)
+{
+  char *s;
+  BOOL quoted;
+
+  if (!ptr) ptr = &last_ptr;
+  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; *s && (quoted || !strchr(sep,*s)); s++)
+    {
+      if (*s == '\"') 
+       quoted = !quoted;
+      else
+       *buff++ = *s;
+    }
+
+  *ptr = (*s) ? s+1 : s;  
+  *buff = 0;
+  last_ptr = *ptr;
+
+  return(True);
+}
+
+/****************************************************************************
+Convert list of tokens to array; dependent on above routine.
+Uses last_ptr from above - bit of a hack.
+****************************************************************************/
+char **toktocliplist(int *ctok, char *sep)
+{
+  char *s=last_ptr;
+  int ictok=0;
+  char **ret, **iret;
+
+  if (!sep) sep = " \t\n\r";
+
+  while(*s && strchr(sep,*s)) s++;
+
+  /* nothing left? */
+  if (!*s) return(NULL);
+
+  do {
+    ictok++;
+    while(*s && (!strchr(sep,*s))) s++;
+    while(*s && strchr(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;
+}
+
+#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,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
+
+
+/****************************************************************************
+prompte a dptr (to make it recently used)
+****************************************************************************/
+void array_promote(char *array,int elsize,int element)
+{
+  char *p;
+  if (element == 0)
+    return;
+
+  p = (char *)malloc(elsize);
+
+  if (!p)
+    {
+      DEBUG(5,("Ahh! Can't malloc\n"));
+      return;
+    }
+  memcpy(p,array + element * elsize, elsize);
+  memmove(array + elsize,array,elsize*element);
+  memcpy(array,p,elsize);
+  free(p);
+}
+
+enum SOCK_OPT_TYPES {OPT_BOOL,OPT_INT,OPT_ON};
+
+struct
+{
+  char *name;
+  int level;
+  int option;
+  int value;
+  int opttype;
+} 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_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
+  {NULL,0,0,0,0}};
+
+       
+
+/****************************************************************************
+set user socket options
+****************************************************************************/
+void set_socket_options(int fd, char *options)
+{
+  string tok;
+
+  while (next_token(&options,tok," \t,"))
+    {
+      int ret=0,i;
+      int value = 1;
+      char *p;
+      BOOL got_value = False;
+
+      if ((p = strchr(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\n",tok));
+    }
+}
+
+
+
+/****************************************************************************
+  close the socket communication
+****************************************************************************/
+void close_sockets(void )
+{
+  close(Client);
+  Client = 0;
+}
+
+/****************************************************************************
+  return the date and time as a string
+****************************************************************************/
+char *timestring(void )
+{
+  static char TimeBuf[100];
+  time_t t;
+  t = time(NULL);
+#ifdef NO_STRFTIME
+  strcpy(TimeBuf, asctime(LocalTime(&t,GMT_TO_LOCAL)));
+#elif defined(CLIX) || defined(CONVEX)
+  strftime(TimeBuf,100,"%m/%d/%y %I:%M:%S %p",LocalTime(&t,GMT_TO_LOCAL));
+#elif defined(AMPM)
+  strftime(TimeBuf,100,"%D %r",LocalTime(&t,GMT_TO_LOCAL));
+#elif defined(TZ_TIME)
+  {
+    strftime(TimeBuf,100,"%D:%T",LocalTime(&t,0));
+    sprintf(TimeBuf+strlen(TimeBuf)," %+03d%02d",
+           -TimeDiff(t)/(60*60),-(TimeDiff(t)/60)%60);
+  }
+#else
+  strftime(TimeBuf,100,"%D %T",LocalTime(&t,GMT_TO_LOCAL));
+#endif
+  return(TimeBuf);
+}
+
+/****************************************************************************
+determine whether we are in the specified group
+****************************************************************************/
+BOOL in_group(gid_t group, int current_gid, int ngroups, int *groups)
+{
+  int i;
+
+  if (group == current_gid) return(True);
+
+  for (i=0;i<ngroups;i++)
+    if (group == groups[i])
+      return(True);
+
+  return(False);
+}
+
+/****************************************************************************
+this is a safer strcpy(), meant to prevent core dumps when nasty things happen
+****************************************************************************/
+char *StrCpy(char *dest,char *src)
+{
+  char *d = dest;
+
+#if AJT
+  /* I don't want to get lazy with these ... */
+  if (!dest || !src) {
+    DEBUG(0,("ERROR: NULL StrCpy() called!\n"));
+    ajt_panic();
+  }
+#endif
+
+  if (!dest) return(NULL);
+  if (!src) {
+    *dest = 0;
+    return(dest);
+  }
+  while ((*d++ = *src++)) ;
+  return(dest);
+}
+
+/****************************************************************************
+line strncpy but always null terminates. Make sure there is room!
+****************************************************************************/
+char *StrnCpy(char *dest,const char *src,int n)
+{
+  char *d = dest;
+  if (!dest) return(NULL);
+  if (!src) {
+    *dest = 0;
+    return(dest);
+  }
+  while (n-- && (*d++ = *src++)) ;
+  *d = 0;
+  return(dest);
+}
+
+
+/*******************************************************************
+copy an IP address from one buffer to another
+********************************************************************/
+void putip(void *dest,void *src)
+{
+  memcpy(dest,src,4);
+}
+
+
+/****************************************************************************
+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 = *(unsigned char *)in++;
+      StrnCpy(out, in, len);
+      out += len;
+      *out=0;
+      in += len;
+    }
+#endif
+  return(ret);
+}
+
+/****************************************************************************
+mangle a name into netbios format
+****************************************************************************/
+int name_mangle(char *In,char *Out,char name_type)
+{
+  fstring name;
+  char buf[20];
+  char *in = (char *)&buf[0];
+  char *out = (char *)Out;
+  char *p, *label;
+  int i;
+
+  if (In[0] != '*') {
+    StrnCpy(name,In,sizeof(name)-1);
+    sprintf(buf,"%-15.15s%c",name,name_type);
+  } else {
+    buf[0]='*';
+    memset(&buf[1],0,16);
+  }
+
+  *out++ = 32;
+  for (i=0;i<16;i++) {
+    char c = toupper(in[i]);
+    out[i*2] = (c>>4) + 'A';
+    out[i*2+1] = (c & 0xF) + 'A';
+  }
+  out[32]=0;
+  out += 32;
+  
+  label = scope;
+  while (*label)
+    {
+      p = strchr(label, '.');
+      if (p == 0)
+       p = label + strlen(label);
+      *out++ = p - label;
+      memcpy(out, label, p - label);
+      out += p - label;
+      label += p - label + (*p == '.');
+    }
+  *out = 0;
+  return(name_len(Out));
+}
+
+
+/*******************************************************************
+  check if a file exists
+********************************************************************/
+BOOL file_exist(char *fname,struct stat *sbuf)
+{
+  struct stat st;
+  if (!sbuf) sbuf = &st;
+  
+  if (sys_stat(fname,sbuf) != 0) 
+    return(False);
+
+  return(S_ISREG(sbuf->st_mode));
+}
+
+/*******************************************************************
+check a files mod time
+********************************************************************/
+time_t file_modtime(char *fname)
+{
+  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,struct stat *st)
+{
+  struct stat st2;
+  if (!st) st = &st2;
+
+  if (sys_stat(dname,st) != 0) 
+    return(False);
+
+  return(S_ISDIR(st->st_mode));
+}
+
+/*******************************************************************
+returns the size in bytes of the named file
+********************************************************************/
+uint32 file_size(char *file_name)
+{
+  struct stat buf;
+  buf.st_size = 0;
+  sys_stat(file_name,&buf);
+  return(buf.st_size);
+}
+
+/****************************************************************************
+check if it's a null mtime
+****************************************************************************/
+static BOOL null_mtime(time_t mtime)
+{
+  if (mtime == 0 || mtime == 0xFFFFFFFF)
+    return(True);
+  return(False);
+}
+
+/*******************************************************************
+  create a 16 bit dos packed date
+********************************************************************/
+static uint16 make_dos_date1(time_t unixdate,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(time_t unixdate,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;
+
+  t = LocalTime(&unixdate,GMT_TO_LOCAL);
+
+  ret = make_dos_date1(unixdate,t);
+  ret = ((ret&0xFFFF)<<16) | make_dos_time1(unixdate,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 = 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 += GMT_TO_LOCAL*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(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_wday = 1;
+  t.tm_yday = 1;
+  t.tm_isdst = -1;
+  
+  /* mktime() also does the local to GMT time conversion for us. XXXXX
+     Do all unixes do this the same?? */
+  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 = IVAL(date_ptr,0);
+  if (!null_mtime(t))
+    t += LOCAL_TO_GMT*TimeDiff(t);
+  return(t);
+}
+
+/*******************************************************************
+return a string representing an attribute for a file
+********************************************************************/
+char *attrib_string(int mode)
+{
+  static char attrstr[10];
+
+  attrstr[0] = 0;
+
+  if (mode & aVOLID) strcat(attrstr,"V");
+  if (mode & aDIR) strcat(attrstr,"D");
+  if (mode & aARCH) strcat(attrstr,"A");
+  if (mode & aHIDDEN) strcat(attrstr,"H");
+  if (mode & aSYSTEM) strcat(attrstr,"S");
+  if (mode & aRONLY) strcat(attrstr,"R");        
+
+  return(attrstr);
+}
+
+
+/*******************************************************************
+  case insensitive string compararison
+********************************************************************/
+int StrCaseCmp(char *s, char *t)
+{
+  for (; tolower(*s) == tolower(*t); ++s, ++t)
+    if (!*s) return 0;
+
+  return tolower(*s) - tolower(*t);
+}
+
+/*******************************************************************
+  case insensitive string compararison, length limited
+********************************************************************/
+int StrnCaseCmp(char *s, char *t, int n)
+{
+  while (n-- && *s && *t) {
+    if (tolower(*s) != tolower(*t)) return(tolower(*s) - tolower(*t));
+    s++; t++;
+  }
+  if (n) return(tolower(*s) - tolower(*t));
+
+  return(0);
+}
+
+/*******************************************************************
+  compare 2 strings 
+********************************************************************/
+BOOL strequal(char *s1,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.
+  ******************************************************************/
+BOOL strnequal(char *s1,char *s2,int 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(char *s1,char *s2)
+{
+  if (s1 == s2) return(True);
+  if (!s1 || !s2) return(False);
+  
+  return(strcmp(s1,s2)==0);
+}
+
+
+/*******************************************************************
+  convert a string to lower case
+********************************************************************/
+void strlower(char *s)
+{
+  while (*s)
+    {
+#ifdef KANJI
+       if (is_shift_jis (*s)) {
+           s += 2;
+       } else if (is_kana (*s)) {
+           s++;
+       } else {
+           if (isupper(*s))
+               *s = tolower(*s);
+           s++;
+       }
+#else
+      if (isupper(*s))
+         *s = tolower(*s);
+      s++;
+#endif /* KANJI */
+    }
+}
+
+/*******************************************************************
+  convert a string to upper case
+********************************************************************/
+void strupper(char *s)
+{
+  while (*s)
+    {
+#ifdef KANJI
+       if (is_shift_jis (*s)) {
+           s += 2;
+       } else if (is_kana (*s)) {
+           s++;
+       } else {
+           if (islower(*s))
+               *s = toupper(*s);
+           s++;
+       }
+#else
+      if (islower(*s))
+       *s = toupper(*s);
+      s++;
+#endif
+    }
+}
+
+/*******************************************************************
+  convert a string to "normal" form
+********************************************************************/
+void strnorm(char *s)
+{
+  if (case_default == CASE_UPPER)
+    strupper(s);
+  else
+    strlower(s);
+}
+
+/*******************************************************************
+check if a string is in "normal" case
+********************************************************************/
+BOOL strisnormal(char *s)
+{
+  if (case_default == CASE_UPPER)
+    return(!strhaslower(s));
+
+  return(!strhasupper(s));
+}
+
+
+/****************************************************************************
+  string replace
+****************************************************************************/
+void string_replace(char *s,char oldc,char newc)
+{
+  while (*s)
+    {
+#ifdef KANJI
+       if (is_shift_jis (*s)) {
+           s += 2;
+       } else if (is_kana (*s)) {
+           s++;
+       } else {
+           if (oldc == *s)
+               *s = newc;
+           s++;
+       }
+#else
+      if (oldc == *s)
+       *s = newc;
+      s++;
+#endif /* KANJI */
+    }
+}
+
+/****************************************************************************
+  make a file into unix format
+****************************************************************************/
+void unix_format(char *fname)
+{
+  pstring namecopy;
+  string_replace(fname,'\\','/');
+#ifndef KANJI
+  dos2unix_format(fname, True);
+#endif /* KANJI */
+
+  if (*fname == '/')
+    {
+      strcpy(namecopy,fname);
+      strcpy(fname,".");
+      strcat(fname,namecopy);
+    }  
+}
+
+/****************************************************************************
+  make a file into dos format
+****************************************************************************/
+void dos_format(char *fname)
+{
+#ifndef KANJI
+  unix2dos_format(fname, True);
+#endif /* KANJI */
+  string_replace(fname,'/','\\');
+}
+
+
+/*******************************************************************
+  show a smb message structure
+********************************************************************/
+void show_msg(char *buf)
+{
+  int i;
+  int bcc=0;
+  if (DEBUGLEVEL < 5)
+    return;
+
+  DEBUG(5,("size=%d\nsmb_com=0x%x\nsmb_rcls=%d\nsmb_reh=%d\nsmb_err=%d\nsmb_flg=%d\nsmb_flg2=%d\n",
+         smb_len(buf),
+         (int)CVAL(buf,smb_com),
+         (int)CVAL(buf,smb_rcls),
+         (int)CVAL(buf,smb_reh),
+         (int)SVAL(buf,smb_err),
+         (int)CVAL(buf,smb_flg),
+         (int)SVAL(buf,smb_flg2)));
+  DEBUG(5,("smb_tid=%d\nsmb_pid=%d\nsmb_uid=%d\nsmb_mid=%d\nsmt_wct=%d\n",
+         (int)SVAL(buf,smb_tid),
+         (int)SVAL(buf,smb_pid),
+         (int)SVAL(buf,smb_uid),
+         (int)SVAL(buf,smb_mid),
+         (int)CVAL(buf,smb_wct)));
+  for (i=0;i<(int)CVAL(buf,smb_wct);i++)
+    DEBUG(5,("smb_vwv[%d]=%d (0x%X)\n",i,
+         SVAL(buf,smb_vwv+2*i),SVAL(buf,smb_vwv+2*i)));
+  bcc = (int)SVAL(buf,smb_vwv+2*(CVAL(buf,smb_wct)));
+  DEBUG(5,("smb_bcc=%d\n",bcc));
+  if (DEBUGLEVEL < 10)
+    return;
+  for (i=0;i<MIN(bcc,128);i++)
+    DEBUG(10,("%X ",CVAL(smb_buf(buf),i)));
+  DEBUG(10,("\n"));  
+}
+
+/*******************************************************************
+  return the length of an smb packet
+********************************************************************/
+int smb_len(char *buf)
+{
+  return( PVAL(buf,3) | (PVAL(buf,2)<<8) | ((PVAL(buf,1)&1)<<16) );
+}
+
+/*******************************************************************
+  set the length of an smb packet
+********************************************************************/
+void _smb_setlen(char *buf,int len)
+{
+  buf[0] = 0;
+  buf[1] = (len&0x10000)>>16;
+  buf[2] = (len&0xFF00)>>8;
+  buf[3] = len&0xFF;
+}
+
+/*******************************************************************
+  set the length and marker of an smb packet
+********************************************************************/
+void smb_setlen(char *buf,int len)
+{
+  _smb_setlen(buf,len);
+
+  CVAL(buf,4) = 0xFF;
+  CVAL(buf,5) = 'S';
+  CVAL(buf,6) = 'M';
+  CVAL(buf,7) = 'B';
+}
+
+/*******************************************************************
+  setup the word count and byte count for a smb message
+********************************************************************/
+int set_message(char *buf,int num_words,int num_bytes,BOOL zero)
+{
+  if (zero)
+    bzero(buf + smb_size,num_words*2 + num_bytes);
+  CVAL(buf,smb_wct) = num_words;
+  SSVAL(buf,smb_vwv + num_words*SIZEOFWORD,num_bytes);  
+  smb_setlen(buf,smb_size + num_words*2 + num_bytes - 4);
+  return (smb_size + num_words*2 + num_bytes);
+}
+
+/*******************************************************************
+return the number of smb words
+********************************************************************/
+int smb_numwords(char *buf)
+{
+  return (CVAL(buf,smb_wct));
+}
+
+/*******************************************************************
+return the size of the smb_buf region of a message
+********************************************************************/
+int smb_buflen(char *buf)
+{
+  return(SVAL(buf,smb_vwv0 + smb_numwords(buf)*2));
+}
+
+/*******************************************************************
+  return a pointer to the smb_buf data area
+********************************************************************/
+int smb_buf_ofs(char *buf)
+{
+  return (smb_size + CVAL(buf,smb_wct)*2);
+}
+
+/*******************************************************************
+  return a pointer to the smb_buf data area
+********************************************************************/
+char *smb_buf(char *buf)
+{
+  return (buf + smb_buf_ofs(buf));
+}
+
+/*******************************************************************
+return the SMB offset into an SMB buffer
+********************************************************************/
+int smb_offset(char *p,char *buf)
+{
+  return(PTR_DIFF(p,buf+4));
+}
+
+
+/*******************************************************************
+skip past some strings in a buffer
+********************************************************************/
+char *skip_string(char *buf,int n)
+{
+  while (n--)
+    buf += strlen(buf) + 1;
+  return(buf);
+}
+
+/*******************************************************************
+trim the specified elements off the front and back of a string
+********************************************************************/
+BOOL trim_string(char *s,char *front,char *back)
+{
+  BOOL ret = False;
+  while (front && *front && strncmp(s,front,strlen(front)) == 0)
+    {
+      char *p = s;
+      ret = True;
+      while (1)
+       {
+         if (!(*p = p[strlen(front)]))
+           break;
+         p++;
+       }
+    }
+  while (back && *back && strlen(s) >= strlen(back) && 
+        (strncmp(s+strlen(s)-strlen(back),back,strlen(back))==0))  
+    {
+      ret = True;
+      s[strlen(s)-strlen(back)] = 0;
+    }
+  return(ret);
+}
+
+
+/*******************************************************************
+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 */
+  string_sub(s, "\\\\", "\\");
+
+  while ((p = strstr(s,"\\..\\")) != NULL)
+    {
+      pstring s1;
+
+      *p = 0;
+      strcpy(s1,p+3);
+
+      if ((p=strrchr(s,'\\')) != NULL)
+       *p = 0;
+      else
+       *s = 0;
+      strcat(s,s1);
+    }  
+
+  trim_string(s,NULL,"\\..");
+
+  string_sub(s, "\\.\\", "\\");
+}
+
+/*******************************************************************
+reduce a file name, removing .. elements. 
+********************************************************************/
+void unix_clean_name(char *s)
+{
+  char *p=NULL;
+
+  DEBUG(3,("unix_clean_name [%s]\n",s));
+
+  /* remove any double slashes */
+  string_sub(s, "//","/");
+
+  while ((p = strstr(s,"/../")) != NULL)
+    {
+      pstring s1;
+
+      *p = 0;
+      strcpy(s1,p+3);
+
+      if ((p=strrchr(s,'/')) != NULL)
+       *p = 0;
+      else
+       *s = 0;
+      strcat(s,s1);
+    }  
+
+  trim_string(s,NULL,"/..");
+}
+
+
+/*******************************************************************
+a wrapper for the normal chdir() function
+********************************************************************/
+int ChDir(char *path)
+{
+  int res;
+  static pstring LastDir="";
+
+  if (strcsequal(path,".")) return(0);
+
+  if (*path == '/' && strcsequal(LastDir,path)) return(0);
+  DEBUG(3,("chdir to %s\n",path));
+  res = sys_chdir(path);
+  if (!res)
+    strcpy(LastDir,path);
+  return(res);
+}
+
+
+/*******************************************************************
+  return the absolute current directory path. A dumb version.
+********************************************************************/
+static char *Dumb_GetWd(char *s)
+{
+#ifdef USE_GETCWD
+    return ((char *)getcwd(s,sizeof(pstring)));
+#else
+    return ((char *)getwd(s));
+#endif
+}
+
+
+/* number of list structures for a caching GetWd function. */
+#define MAX_GETWDCACHE (50)
+
+struct
+{
+  ino_t inode;
+  dev_t dev;
+  char *text;
+  BOOL valid;
+} ino_list[MAX_GETWDCACHE];
+
+BOOL use_getwd_cache=True;
+
+/*******************************************************************
+  return the absolute current directory path
+********************************************************************/
+char *GetWd(char *str)
+{
+  pstring s;
+  static BOOL getwd_cache_init = False;
+  struct stat st, st2;
+  int i;
+
+  *s = 0;
+
+  if (!use_getwd_cache)
+    return(Dumb_GetWd(str));
+
+  /* init the cache */
+  if (!getwd_cache_init)
+    {
+      getwd_cache_init = True;
+      for (i=0;i<MAX_GETWDCACHE;i++)
+       {
+         string_init(&ino_list[i].text,"");
+         ino_list[i].valid = False;
+       }
+    }
+
+  /*  Get the inode of the current directory, if this doesn't work we're
+      in trouble :-) */
+
+  if (stat(".",&st) == -1) 
+    {
+      DEBUG(0,("Very strange, couldn't stat \".\"\n"));
+      return(Dumb_GetWd(str));
+    }
+
+
+  for (i=0; i<MAX_GETWDCACHE; i++)
+    if (ino_list[i].valid)
+      {
+
+       /*  If we have found an entry with a matching inode and dev number
+           then find the inode number for the directory in the cached string.
+           If this agrees with that returned by the stat for the current
+           directory then all is o.k. (but make sure it is a directory all
+           the same...) */
+      
+       if (st.st_ino == ino_list[i].inode &&
+           st.st_dev == ino_list[i].dev)
+         {
+           if (stat(ino_list[i].text,&st2) == 0)
+             {
+               if (st.st_ino == st2.st_ino &&
+                   st.st_dev == st2.st_dev &&
+                   (st2.st_mode & S_IFMT) == S_IFDIR)
+                 {
+                   strcpy (str, ino_list[i].text);
+
+                   /* promote it for future use */
+                   array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
+                   return (str);
+                 }
+               else
+                 {
+                   /*  If the inode is different then something's changed, 
+                       scrub the entry and start from scratch. */
+                   ino_list[i].valid = False;
+                 }
+             }
+         }
+      }
+
+
+  /*  We don't have the information to hand so rely on traditional methods.
+      The very slow getcwd, which spawns a process on some systems, or the
+      not quite so bad getwd. */
+
+  if (!Dumb_GetWd(s))
+    {
+      DEBUG(0,("Getwd failed, errno %d\n",errno));
+      return (NULL);
+    }
+
+  strcpy(str,s);
+
+  DEBUG(5,("GetWd %s, inode %d, dev %x\n",s,(int)st.st_ino,(int)st.st_dev));
+
+  /* add it to the cache */
+  i = MAX_GETWDCACHE - 1;
+  string_set(&ino_list[i].text,s);
+  ino_list[i].dev = st.st_dev;
+  ino_list[i].inode = st.st_ino;
+  ino_list[i].valid = True;
+
+  /* put it at the top of the list */
+  array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
+
+  return (str);
+}
+
+
+
+/*******************************************************************
+reduce a file name, removing .. elements and checking that 
+it is below dir in the heirachy. This uses GetWd() and so must be run
+on the system that has the referenced file system.
+
+widelinks are allowed if widelinks is true
+********************************************************************/
+BOOL reduce_name(char *s,char *dir,BOOL widelinks)
+{
+#ifndef REDUCE_PATHS
+  return True;
+#else
+  pstring dir2;
+  pstring wd;
+  pstring basename;
+  pstring newname;
+  char *p=NULL;
+  BOOL relative = (*s != '/');
+
+  *dir2 = *wd = *basename = *newname = 0;
+
+  if (widelinks)
+    {
+      unix_clean_name(s);
+      /* can't have a leading .. */
+      if (strncmp(s,"..",2) == 0 && (s[2]==0 || s[2]=='/'))
+       {
+         DEBUG(3,("Illegal file name? (%s)\n",s));
+         return(False);
+       }
+      return(True);
+    }
+  
+  DEBUG(3,("reduce_name [%s] [%s]\n",s,dir));
+
+  /* remove any double slashes */
+  string_sub(s,"//","/");
+
+  strcpy(basename,s);
+  p = strrchr(basename,'/');
+
+  if (!p)
+    return(True);
+
+  if (!GetWd(wd))
+    {
+      DEBUG(0,("couldn't getwd for %s %s\n",s,dir));
+      return(False);
+    }
+
+  if (ChDir(dir) != 0)
+    {
+      DEBUG(0,("couldn't chdir to %s\n",dir));
+      return(False);
+    }
+
+  if (!GetWd(dir2))
+    {
+      DEBUG(0,("couldn't getwd for %s\n",dir));
+      ChDir(wd);
+      return(False);
+    }
+
+
+    if (p && (p != basename))
+      {
+       *p = 0;
+       if (strcmp(p+1,".")==0)
+         p[1]=0;
+       if (strcmp(p+1,"..")==0)
+         *p = '/';
+      }
+
+  if (ChDir(basename) != 0)
+    {
+      ChDir(wd);
+      DEBUG(3,("couldn't chdir for %s %s basename=%s\n",s,dir,basename));
+      return(False);
+    }
+
+  if (!GetWd(newname))
+    {
+      ChDir(wd);
+      DEBUG(2,("couldn't get wd for %s %s\n",s,dir2));
+      return(False);
+    }
+
+  if (p && (p != basename))
+    {
+      strcat(newname,"/");
+      strcat(newname,p+1);
+    }
+
+  {
+    int l = strlen(dir2);    
+    if (dir2[l-1] == '/')
+      l--;
+
+    if (strncmp(newname,dir2,l) != 0)
+      {
+       ChDir(wd);
+       DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n",s,dir2,newname,l));
+       return(False);
+      }
+
+    if (relative)
+      {
+       if (newname[l] == '/')
+         strcpy(s,newname + l + 1);
+       else
+         strcpy(s,newname+l);
+      }
+    else
+      strcpy(s,newname);
+  }
+
+  ChDir(wd);
+
+  if (strlen(s) == 0)
+    strcpy(s,"./");
+
+  DEBUG(3,("reduced to %s\n",s));
+  return(True);
+#endif
+}
+
+/****************************************************************************
+expand some *s 
+****************************************************************************/
+static void expand_one(char *Mask,int len)
+{
+  char *p1;
+  while ((p1 = strchr(Mask,'*')) != NULL)
+    {
+      int lfill = (len+1) - strlen(Mask);
+      int l1= (p1 - Mask);
+      pstring tmp;
+      strcpy(tmp,Mask);  
+      memset(tmp+l1,'?',lfill);
+      strcpy(tmp + l1 + lfill,Mask + l1 + 1);  
+      strcpy(Mask,tmp);      
+    }
+}
+
+/****************************************************************************
+expand a wildcard expression, replacing *s with ?s
+****************************************************************************/
+void expand_mask(char *Mask,BOOL doext)
+{
+  pstring mbeg,mext;
+  pstring dirpart;
+  pstring filepart;
+  BOOL hasdot = False;
+  char *p1;
+  BOOL absolute = (*Mask == '\\');
+
+  *mbeg = *mext = *dirpart = *filepart = 0;
+
+  /* parse the directory and filename */
+  if (strchr(Mask,'\\'))
+    dirname_dos(Mask,dirpart);
+
+  filename_dos(Mask,filepart);
+
+  strcpy(mbeg,filepart);
+  if ((p1 = strchr(mbeg,'.')) != NULL)
+    {
+      hasdot = True;
+      *p1 = 0;
+      p1++;
+      strcpy(mext,p1);
+    }
+  else
+    {
+      strcpy(mext,"");
+      if (strlen(mbeg) > 8)
+       {
+         strcpy(mext,mbeg + 8);
+         mbeg[8] = 0;
+       }
+    }
+
+  if (*mbeg == 0)
+    strcpy(mbeg,"????????");
+  if ((*mext == 0) && doext && !hasdot)
+    strcpy(mext,"???");
+
+  if (strequal(mbeg,"*") && *mext==0) 
+    strcpy(mext,"*");
+
+  /* expand *'s */
+  expand_one(mbeg,8);
+  if (*mext)
+    expand_one(mext,3);
+
+  strcpy(Mask,dirpart);
+  if (*dirpart || absolute) strcat(Mask,"\\");
+  strcat(Mask,mbeg);
+  strcat(Mask,".");
+  strcat(Mask,mext);
+
+  DEBUG(6,("Mask expanded to [%s]\n",Mask));
+}  
+
+
+/****************************************************************************
+does a string have any uppercase chars in it?
+****************************************************************************/
+BOOL strhasupper(char *s)
+{
+  while (*s) 
+    {
+#ifdef KANJI
+       if (is_shift_jis (*s)) {
+           s += 2;
+       } else if (is_kana (*s)) {
+           s++;
+       } else {
+           if (isupper(*s)) return(True);
+           s++;
+       }
+#else 
+      if (isupper(*s)) return(True);
+      s++;
+#endif /* KANJI */
+    }
+  return(False);
+}
+
+/****************************************************************************
+does a string have any lowercase chars in it?
+****************************************************************************/
+BOOL strhaslower(char *s)
+{
+  while (*s) 
+    {
+#ifdef KANJI
+       if (is_shift_jis (*s)) {
+           s += 2;
+       } else if (is_kana (*s)) {
+           s++;
+       } else {
+           if (islower(*s)) return(True);
+           s++;
+       }
+#else 
+      if (islower(*s)) return(True);
+      s++;
+#endif /* KANJI */
+    }
+  return(False);
+}
+
+/****************************************************************************
+find the number of chars in a string
+****************************************************************************/
+int count_chars(char *s,char c)
+{
+  int count=0;
+  while (*s) 
+    {
+      if (*s == c)
+       count++;
+      s++;
+    }
+  return(count);
+}
+
+
+/****************************************************************************
+  make a dir struct
+****************************************************************************/
+void make_dir_struct(char *buf,char *mask,char *fname,unsigned int size,int mode,time_t date)
+{  
+  char *p;
+  pstring mask2;
+
+  strcpy(mask2,mask);
+
+  if ((mode & aDIR) != 0)
+    size = 0;
+
+  memset(buf+1,' ',11);
+  if ((p = strchr(mask2,'.')) != NULL)
+    {
+      *p = 0;
+      memcpy(buf+1,mask2,MIN(strlen(mask2),8));
+      memcpy(buf+9,p+1,MIN(strlen(p+1),3));
+      *p = '.';
+    }
+  else
+    memcpy(buf+1,mask2,MIN(strlen(mask2),11));
+
+  bzero(buf+21,DIR_STRUCT_SIZE-21);
+  CVAL(buf,21) = mode;
+  put_dos_date(buf,22,date);
+  SSVAL(buf,26,size & 0xFFFF);
+  SSVAL(buf,28,size >> 16);
+  StrnCpy(buf+30,fname,12);
+  if (!case_sensitive)
+    strupper(buf+30);
+  DEBUG(8,("put name [%s] into dir struct\n",buf+30));
+}
+
+
+/*******************************************************************
+close the low 3 fd's and open dev/null in their place
+********************************************************************/
+void close_low_fds(void)
+{
+  int fd;
+  int i;
+  close(0); close(1); 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++) {
+    fd = open("/dev/null",O_RDWR,0);
+    if (fd < 0) fd = 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;
+    }
+  }
+}
+
+
+/****************************************************************************
+write to a socket
+****************************************************************************/
+int write_socket(int fd,char *buf,int len)
+{
+  int ret=0;
+
+  if (passive)
+    return(len);
+  DEBUG(6,("write_socket(%d,%d)\n",fd,len));
+  ret = write_data(fd,buf,len);
+      
+  DEBUG(6,("write_socket(%d,%d) wrote %d\n",fd,len,ret));
+  return(ret);
+}
+
+/****************************************************************************
+read from a socket
+****************************************************************************/
+int read_udp_socket(int fd,char *buf,int len)
+{
+  int ret;
+  struct sockaddr sock;
+  int socklen;
+  
+  socklen = sizeof(sock);
+  bzero((char *)&sock,socklen);
+  bzero((char *)&lastip,sizeof(lastip));
+  ret = recvfrom(fd,buf,len,0,&sock,&socklen);
+  if (ret <= 0)
+    {
+      DEBUG(2,("read socket failed. ERRNO=%d\n",errno));
+      return(0);
+    }
+
+  lastip = *(struct in_addr *) &sock.sa_data[2];
+  lastport = ntohs(((struct sockaddr_in *)&sock)->sin_port);
+
+  return(ret);
+}
+
+/****************************************************************************
+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 = fcntl(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 fcntl( fd, F_SETFL, val);
+#undef FLAG_TO_SET
+}
+
+
+/****************************************************************************
+Calculate the difference in timeout values. Return 1 if val1 > val2,
+0 if val1 == val2, -1 if val1 < val2. Stores result in retval. retval
+may be == val1 or val2
+****************************************************************************/
+static int tval_sub( struct timeval *retval, struct timeval *val1, struct timeval *val2)
+{
+  int usecdiff = val1->tv_usec - val2->tv_usec;
+  int secdiff = val1->tv_sec - val2->tv_sec;
+  if(usecdiff < 0) {
+    usecdiff = 1000000 + usecdiff;
+    secdiff--;
+  }
+  retval->tv_sec = secdiff;
+  retval->tv_usec = usecdiff;
+  if(secdiff < 0)
+    return -1;
+  if(secdiff > 0)
+    return 1;
+  return (usecdiff < 0 ) ? -1 : ((usecdiff > 0 ) ? 1 : 0);
+}
+
+/****************************************************************************
+read data from a device with a timout in msec.
+mincount = if timeout, minimum to read before returning
+maxcount = number to be read.
+****************************************************************************/
+int read_with_timeout(int fd,char *buf,int mincnt,int maxcnt,long time_out,BOOL exact)
+{
+  fd_set fds;
+  int selrtn;
+  int readret;
+  int nread = 0;
+  struct timeval timeout, tval1, tval2, tvaldiff;
+  int error_limit = 5;
+
+  /* just checking .... */
+  if (maxcnt <= 0) return(0);
+
+  if(time_out == -2)
+    time_out = DEFAULT_PIPE_TIMEOUT;
+
+  /* Blocking read */
+  if(time_out < 0) {
+    if (mincnt == 0) mincnt = maxcnt;
+
+    while (nread < mincnt)
+      {
+       readret = read(fd, buf + nread, maxcnt - nread);
+       if (readret <= 0) return(nread);
+       nread += readret;
+      }
+    return(nread);
+  }
+  
+  /* Non blocking read */
+  if(time_out == 0) {
+    set_blocking(fd, False);
+    nread = read_data(fd, buf, mincnt);
+    if (nread < maxcnt)
+      nread += read(fd,buf+nread,maxcnt-nread);
+    if(nread == -1 && errno == EWOULDBLOCK)
+      nread = 0;
+    set_blocking(fd,True);
+    return nread;
+  }
+
+  /* Most difficult - timeout read */
+  /* If this is ever called on a disk file and 
+        mincnt is greater then the filesize then
+        system performance will suffer severely as 
+        select always return true on disk files */
+
+  /* Set initial timeout */
+  timeout.tv_sec = time_out / 1000;
+  timeout.tv_usec = 1000 * (time_out % 1000);
+
+  /* As most UNIXes don't modify the value of timeout
+     when they return from select we need to get the timeofday (in usec)
+     now, and also after the select returns so we know
+     how much time has elapsed */
+
+  if (exact)
+    GetTimeOfDay( &tval1);
+  nread = 0; /* Number of bytes we have read */
+
+  for(;;) 
+    {      
+      FD_ZERO(&fds);
+      FD_SET(fd,&fds);
+      
+      selrtn = sys_select(&fds,&timeout);
+      
+      /* Check if error */
+      if(selrtn == -1) {
+       errno = EBADF;
+       return -1;
+      }
+      
+      /* Did we timeout ? */
+      if (selrtn == 0) {
+       if (nread < mincnt) return -1;
+       break; /* Yes */
+      }
+      
+      readret = read(fd, buf+nread, maxcnt-nread);
+      if (readret == 0 && nread < mincnt) {
+       /* error_limit should not really be needed, but some systems
+          do strange things ...  I don't want to just continue
+          indefinately in case we get an infinite loop */
+       if (error_limit--) continue;
+       return(-1);
+      }
+
+      if (readret < 0) {
+       /* force a particular error number for
+          portability */
+       DEBUG(5,("read gave error %s\n",strerror(errno)));
+       errno = EBADF;
+       return -1;
+      }
+      
+      nread += readret;
+      
+      /* If we have read more than mincnt then return */
+      if (nread >= mincnt)
+       break;
+
+      /* We need to do another select - but first reduce the
+        time_out by the amount of time already elapsed - if
+        this is less than zero then return */
+      if (exact) {
+       GetTimeOfDay(&tval2);
+       (void)tval_sub( &tvaldiff, &tval2, &tval1);
+      
+       if (tval_sub(&timeout, &timeout, &tvaldiff) <= 0) 
+         break; /* We timed out */
+      }
+      
+      /* Save the time of day as we need to do the select 
+        again (saves a system call) */
+      tval1 = tval2;
+    }
+
+  /* Return the number we got */
+  return(nread);
+}
+
+/****************************************************************************
+read data from the client. Maxtime is in milliseconds
+****************************************************************************/
+int read_max_udp(int fd,char *buffer,int bufsize,int maxtime)
+{
+  fd_set fds;
+  int selrtn;
+  int nread;
+  struct timeval timeout;
+  FD_ZERO(&fds);
+  FD_SET(fd,&fds);
+
+  timeout.tv_sec = maxtime / 1000;
+  timeout.tv_usec = (maxtime % 1000) * 1000;
+
+  selrtn = sys_select(&fds,maxtime>0?&timeout:NULL);
+
+  if (!FD_ISSET(fd,&fds))
+    return 0;
+
+  nread = read_udp_socket(fd, buffer, bufsize);
+
+  /* return the number got */
+  return(nread);
+}
+
+/*******************************************************************
+find the difference in milliseconds between two struct timeval
+values
+********************************************************************/
+int TvalDiff(struct timeval *tvalold,struct timeval *tvalnew)
+{
+  return((tvalnew->tv_sec - tvalold->tv_sec)*1000 + 
+        ((int)tvalnew->tv_usec - (int)tvalold->tv_usec)/1000);  
+}
+
+/****************************************************************************
+send a keepalive packet (rfc1002)
+****************************************************************************/
+BOOL send_keepalive(int client)
+{
+  unsigned char buf[4];
+
+  buf[0] = 0x85;
+  buf[1] = buf[2] = buf[3] = 0;
+
+  return(write_data(client,(char *)buf,4) == 4);
+}
+
+
+
+/****************************************************************************
+  read data from the client, reading exactly N bytes. 
+****************************************************************************/
+int read_data(int fd,char *buffer,int N)
+{
+  int  ret;
+  int total=0;  
+  while (total < N)
+    {
+      ret = read(fd,buffer + total,N - total);
+
+      /* this is for portability */
+      if (ret < 0)
+       errno = EBADF;
+
+      if (ret <= 0)
+       return total;
+      total += ret;
+    }
+  return total;
+}
+
+
+/****************************************************************************
+  write data to a fd 
+****************************************************************************/
+int write_data(int fd,char *buffer,int N)
+{
+  int total=0;
+  int ret;
+
+  while (total < N)
+    {
+      ret = write(fd,buffer + total,N - total);
+
+      if (ret <= 0)
+       return total;
+
+      total += ret;
+    }
+  return total;
+}
+
+
+/* variables used by the read prediction module */
+int rp_fd = -1;
+int rp_offset = 0;
+int rp_length = 0;
+int rp_alloced = 0;
+int rp_predict_fd = -1;
+int rp_predict_offset = 0;
+int rp_predict_length = 0;
+int rp_timeout = 5;
+time_t rp_time = 0;
+char *rp_buffer = NULL;
+BOOL predict_skip=False;
+time_t smb_last_time=(time_t)0;
+
+/****************************************************************************
+handle read prediction on a file
+****************************************************************************/
+int read_predict(int fd,int offset,char *buf,char **ptr,int num)
+{
+  int ret = 0;
+  int possible = rp_length - (offset - rp_offset);
+
+  possible = MIN(possible,num);
+
+  /* give data if possible */
+  if (fd == rp_fd && 
+      offset >= rp_offset && 
+      possible>0 &&
+      smb_last_time-rp_time < rp_timeout)
+    {
+      ret = possible;
+      if (buf)
+       memcpy(buf,rp_buffer + (offset-rp_offset),possible);
+      else
+       *ptr = rp_buffer + (offset-rp_offset);
+      DEBUG(5,("read-prediction gave %d bytes of %d\n",ret,num));
+    }
+
+  if (ret == num) {
+    predict_skip = True;
+  } else {
+    predict_skip = False;
+
+    /* prepare the next prediction */
+    rp_predict_fd = fd;
+    rp_predict_offset = offset + num;
+    rp_predict_length = num;
+  }
+
+  if (ret < 0) ret = 0;
+
+  return(ret);
+}
+
+/****************************************************************************
+pre-read some data
+****************************************************************************/
+void do_read_prediction()
+{
+  if (predict_skip) return;
+
+  if (rp_predict_fd == -1) 
+    return;
+
+  rp_fd = rp_predict_fd;
+  rp_offset = rp_predict_offset;
+  rp_length = 0;
+
+  rp_predict_fd = -1;
+
+  rp_predict_length = MIN(rp_predict_length,2*ReadSize);
+  rp_predict_length = MAX(rp_predict_length,1024);
+  rp_offset = (rp_offset/1024)*1024;
+  rp_predict_length = (rp_predict_length/1024)*1024;
+
+  if (rp_predict_length > rp_alloced)
+    {
+      rp_buffer = Realloc(rp_buffer,rp_predict_length);
+      rp_alloced = rp_predict_length;
+      if (!rp_buffer)
+       {
+         DEBUG(0,("can't allocate read-prediction buffer\n"));
+         rp_predict_fd = -1;
+         rp_fd = -1;
+         rp_alloced = 0;
+         return;
+       }
+    }
+
+  if (lseek(rp_fd,rp_offset,SEEK_SET) != rp_offset) {
+    rp_fd = -1;
+    rp_predict_fd = -1;
+    return;
+  }
+
+  rp_length = read(rp_fd,rp_buffer,rp_predict_length);
+  rp_time = time(NULL);
+  if (rp_length < 0)
+    rp_length = 0;
+}
+
+/****************************************************************************
+invalidate read-prediction on a fd
+****************************************************************************/
+void invalidate_read_prediction(int fd)
+{
+ if (rp_fd == fd) 
+   rp_fd = -1;
+ if (rp_predict_fd == fd)
+   rp_predict_fd = -1;
+}
+
+
+/****************************************************************************
+transfer some data between two fd's
+****************************************************************************/
+int transfer_file(int infd,int outfd,int n,char *header,int headlen,int align)
+{
+  static char *buf=NULL;  
+  char *buf1,*abuf;
+  static int size = 0;
+  int total = 0;
+
+  DEBUG(4,("transfer_file %d  (head=%d) called\n",n,headlen));
+
+  if ((size < ReadSize) && buf) {
+    free(buf);
+    buf = NULL;
+  }
+
+  size = MAX(ReadSize,1024);
+
+  while (!buf && size>0) {
+    buf = (char *)Realloc(buf,size+8);
+    if (!buf) size /= 2;
+  }
+  if (!buf) {
+    DEBUG(0,("Can't allocate transfer buffer!\n"));
+    exit(1);
+  }
+
+  abuf = buf + (align%8);
+
+  if (header)
+    n += headlen;
+
+  while (n > 0)
+    {
+      int s = MIN(n,size);
+      int ret,ret2=0;
+
+      ret = 0;
+
+      if (header && (headlen >= MIN(s,1024))) {
+       buf1 = header;
+       s = headlen;
+       ret = headlen;
+       headlen = 0;
+       header = NULL;
+      } else {
+       buf1 = abuf;
+      }
+
+      if (header && headlen > 0)
+       {
+         ret = MIN(headlen,size);
+         memcpy(buf1,header,ret);
+         headlen -= ret;
+         header += ret;
+         if (headlen <= 0) header = NULL;
+       }
+
+      if (s > ret)
+       ret += read(infd,buf1+ret,s-ret);
+
+      if (ret > 0)
+       {
+         ret2 = (outfd>=0?write_data(outfd,buf1,ret):ret);
+         if (ret2 > 0) total += ret2;
+         /* if we can't write then dump excess data */
+         if (ret2 != ret)
+           transfer_file(infd,-1,n-(ret+headlen),NULL,0,0);
+       }
+      if (ret <= 0 || ret2 != ret)
+       return(total);
+      n -= ret;
+    }
+  return(total);
+}
+
+
+/****************************************************************************
+read 4 bytes of a smb packet and return the smb length of the packet
+possibly store the result in the buffer
+****************************************************************************/
+int read_smb_length(int fd,char *inbuf,int timeout)
+{
+  char *buffer;
+  char buf[4];
+  int len=0, msg_type;
+  BOOL ok=False;
+
+  if (inbuf)
+    buffer = inbuf;
+  else
+    buffer = buf;
+
+  while (!ok)
+    {
+      if (timeout > 0)
+       ok = (read_with_timeout(fd,buffer,4,4,timeout,False) == 4);
+      else     
+       ok = (read_data(fd,buffer,4) == 4);
+
+      if (!ok)
+       {
+         if (timeout>0)
+           {
+             DEBUG(10,("select timeout (%d)\n", timeout));
+             return(-1);
+           }
+         else
+           {
+             DEBUG(6,("couldn't read from client\n"));
+             exit(1);
+           }
+       }
+
+      len = smb_len(buffer);
+      msg_type = CVAL(buffer,0);
+
+      if (msg_type == 0x85) 
+       {
+         DEBUG(5,( "Got keepalive packet\n"));
+         ok = False;
+       }
+    }
+
+  DEBUG(10,("got smb length of %d\n",len));
+
+  return(len);
+}
+
+
+
+/****************************************************************************
+  read an smb from a fd and return it's length
+The timeout is in milli seconds
+****************************************************************************/
+BOOL receive_smb(int fd,char *buffer,int timeout)
+{
+  int len;
+  BOOL ok;
+
+  bzero(buffer,smb_size + 100);
+
+  len = read_smb_length(fd,buffer,timeout);
+  if (len == -1)
+    return(False);
+
+  if (len > BUFFER_SIZE)
+    {
+      DEBUG(0,("Invalid packet length! (%d bytes)\n",len));
+      if (len > BUFFER_SIZE + (SAFETY_MARGIN/2))
+       exit(1);
+    }
+
+  ok = (read_data(fd,buffer+4,len) == len);
+
+  if (!ok)
+    {
+      close_sockets();
+      exit(1);
+    }
+
+  return(True);
+}
+
+
+/****************************************************************************
+  send an smb to a fd 
+****************************************************************************/
+BOOL send_smb(int fd,char *buffer)
+{
+  int len;
+  int ret,nwritten=0;
+  len = smb_len(buffer) + 4;
+
+  while (nwritten < len)
+    {
+      ret = write_socket(fd,buffer+nwritten,len - nwritten);
+      if (ret <= 0)
+       {
+         DEBUG(0,("Error writing %d bytes to client. %d. Exiting\n",len,ret));
+          close_sockets();
+         exit(1);
+       }
+      nwritten += ret;
+    }
+
+
+  return True;
+}
+
+
+/****************************************************************************
+find a pointer to a netbios name
+****************************************************************************/
+char *name_ptr(char *buf,int ofs)
+{
+  unsigned char c = *(unsigned char *)(buf+ofs);
+
+  if ((c & 0xC0) == 0xC0)
+    {
+      uint16 l;
+      char p[2];
+      memcpy(p,buf+ofs,2);
+      p[0] &= ~0xC0;
+      l = RSVAL(p,0);
+      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);
+  strcpy(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 *s)
+{
+  char *s0=s;
+  unsigned char c = *(unsigned char *)s;
+  if ((c & 0xC0) == 0xC0)
+    return(2);
+  while (*s) s += (*s)+1;
+  return(PTR_DIFF(s,s0)+1);
+}
+
+/****************************************************************************
+send a single packet to a port on another machine
+****************************************************************************/
+BOOL send_one_packet(char *buf,int len,struct in_addr ip,int port,int type)
+{
+  BOOL ret;
+  int out_fd;
+  struct sockaddr_in sock_out;
+
+  if (passive)
+    return(True);
+
+  /* create a socket to write to */
+  out_fd = socket(AF_INET, type, 0);
+  if (out_fd == -1) 
+    {
+      DEBUG(0,("socket failed"));
+      return False;
+    }
+
+  /* set the address and port */
+  bzero((char *)&sock_out,sizeof(sock_out));
+  putip((char *)&sock_out.sin_addr,(char *)&ip);
+  sock_out.sin_port = htons( port );
+  sock_out.sin_family = AF_INET;
+  
+  if (DEBUGLEVEL > 0)
+    DEBUG(3,("sending a packet of len %d to (%s) on port %d of type %s\n",
+            len,inet_ntoa(ip),port,type==SOCK_DGRAM?"DGRAM":"STREAM"));
+       
+  /* send it */
+  ret = (sendto(out_fd,buf,len,0,(struct sockaddr *)&sock_out,sizeof(sock_out)) >= 0);
+
+  if (!ret)
+    DEBUG(0,("Packet send to %s(%d) failed ERRNO=%d\n",
+            inet_ntoa(ip),port,errno));
+
+  close(out_fd);
+  return(ret);
+}
+
+/*******************************************************************
+sleep for a specified number of milliseconds
+********************************************************************/
+void msleep(int t)
+{
+  int tdiff=0;
+  struct timeval tval,t1,t2;  
+  fd_set fds;
+
+  GetTimeOfDay(&t1);
+  GetTimeOfDay(&t2);
+  
+  while (tdiff < t) {
+    tval.tv_sec = (t-tdiff)/1000;
+    tval.tv_usec = 1000*((t-tdiff)%1000);
+    FD_ZERO(&fds);
+    errno = 0;
+    sys_select(&fds,&tval);
+
+    GetTimeOfDay(&t2);
+    tdiff = TvalDiff(&t1,&t2);
+  }
+}
+
+/****************************************************************************
+check if a string is part of a list
+****************************************************************************/
+BOOL in_list(char *s,char *list,BOOL casesensitive)
+{
+  pstring tok;
+  char *p=list;
+
+  if (!list) return(False);
+
+  while (next_token(&p,tok,LIST_SEP))
+    {
+      if (casesensitive) {
+       if (strcmp(tok,s) == 0)
+         return(True);
+      } else {
+       if (StrCaseCmp(tok,s) == 0)
+         return(True);
+      }
+    }
+  return(False);
+}
+
+/* this is used to prevent lots of mallocs of size 1 */
+static char *null_string = NULL;
+
+/****************************************************************************
+set a string value, allocing the space for the string
+****************************************************************************/
+BOOL string_init(char **dest,char *src)
+{
+  int l;
+  if (!src)     
+    src = "";
+
+  l = strlen(src);
+
+  if (l == 0)
+    {
+      if (!null_string)
+       null_string = (char *)malloc(1);
+
+      *null_string = 0;
+      *dest = null_string;
+    }
+  else
+    {
+      *dest = (char *)malloc(l+1);
+      strcpy(*dest,src);
+    }
+  return(True);
+}
+
+/****************************************************************************
+free a string value
+****************************************************************************/
+void string_free(char **s)
+{
+  if (!s || !(*s)) return;
+  if (*s == null_string)
+    *s = NULL;
+  if (*s) free(*s);
+  *s = NULL;
+}
+
+/****************************************************************************
+set a string value, allocing the space for the string, and deallocating any 
+existing space
+****************************************************************************/
+BOOL string_set(char **dest,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.
+
+return True if a substitution was done.
+****************************************************************************/
+BOOL string_sub(char *s,char *pattern,char *insert)
+{
+  BOOL ret = False;
+  char *p;
+  int ls,lp,li;
+
+  if (!insert || !pattern || !s) return(False);
+
+  ls = strlen(s);
+  lp = strlen(pattern);
+  li = strlen(insert);
+
+  if (!*pattern) return(False);
+
+  while (lp <= ls && (p = strstr(s,pattern)))
+    {
+      ret = True;
+      memmove(p+li,p+lp,ls + 1 - (PTR_DIFF(p,s) + lp));
+      memcpy(p,insert,li);
+      s = p + li;
+      ls = strlen(s);
+    }
+  return(ret);
+}
+
+
+
+/*********************************************************
+* Recursive routine that is called by mask_match.
+* Does the actual matching.
+*********************************************************/
+BOOL do_match(char *str, char *regexp, int case_sig)
+{
+  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 && (case_sig ? (*p != *str) : (toupper(*p)!=toupper(*str))))
+         str++;
+       if(do_match(str,p,case_sig))
+         return True;
+       if(!*str)
+         return False;
+       else
+         str++;
+      }
+      return False;
+
+    default:
+      if(case_sig) {
+       if(*str != *p)
+         return False;
+      } else {
+       if(toupper(*str) != toupper(*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;
+}
+
+
+/*********************************************************
+* Routine to match a given string with a regexp - uses
+* simplified regexp that takes * and ? only. Case can be
+* significant or not.
+*********************************************************/
+BOOL mask_match(char *str, char *regexp, int case_sig,BOOL trans2)
+{
+  char *p;
+  pstring p1, p2;
+  fstring ebase,eext,sbase,sext;
+
+  BOOL matched;
+
+  /* Make local copies of str and regexp */
+  StrnCpy(p1,regexp,sizeof(pstring)-1);
+  StrnCpy(p2,str,sizeof(pstring)-1);
+
+  if (!strchr(p2,'.')) {
+    strcat(p2,".");
+  }
+
+/*
+  if (!strchr(p1,'.')) {
+    strcat(p1,".");
+  }
+*/
+
+#if 0
+  if (strchr(p1,'.'))
+    {
+      string_sub(p1,"*.*","*");
+      string_sub(p1,".*","*");
+    }
+#endif
+
+  /* Remove any *? and ** as they are meaningless */
+  for(p = p1; *p; p++)
+    while( *p == '*' && (p[1] == '?' ||p[1] == '*'))
+      (void)strcpy( &p[1], &p[2]);
+
+  if (strequal(p1,"*")) return(True);
+
+  DEBUG(5,("mask_match str=<%s> regexp=<%s>, case_sig = %d\n", p2, p1, case_sig));
+
+  if (trans2) {
+    strcpy(ebase,p1);
+    strcpy(sbase,p2);
+  } else {
+    if ((p=strrchr(p1,'.'))) {
+      *p = 0;
+      strcpy(ebase,p1);
+      strcpy(eext,p+1);
+    } else {
+      strcpy(ebase,p1);
+      eext[0] = 0;
+    }
+
+  if (!strequal(p2,".") && !strequal(p2,"..") && (p=strrchr(p2,'.'))) {
+    *p = 0;
+    strcpy(sbase,p2);
+    strcpy(sext,p+1);
+  } else {
+    strcpy(sbase,p2);
+    strcpy(sext,"");
+  }
+  }
+
+  matched = do_match(sbase,ebase,case_sig) && 
+    (trans2 || do_match(sext,eext,case_sig));
+
+  DEBUG(5,("mask_match returning %d\n", matched));
+
+  return matched;
+}
+
+
+
+/****************************************************************************
+become a daemon, discarding the controlling terminal
+****************************************************************************/
+void become_daemon(void)
+{
+#ifndef NO_FORK_DEBUG
+  if (fork())
+    exit(0);
+
+  /* detach from the terminal */
+#ifdef USE_SETSID
+  setsid();
+#else
+#ifdef TIOCNOTTY
+  {
+    int i = open("/dev/tty", O_RDWR);
+    if (i >= 0) 
+      {
+       ioctl(i, (int) TIOCNOTTY, (char *)0);      
+       close(i);
+      }
+  }
+#endif
+#endif
+#endif
+}
+
+/****************************************************************************
+calculate the default netmask for an address
+****************************************************************************/
+static void default_netmask(struct in_addr *inm, struct in_addr *iad)
+{
+  unsigned long ad = ntohl(iad->s_addr);
+  unsigned long nm;
+  /*
+  ** Guess a netmask based on the class of the IP address given.
+  */
+  if ( (ad & 0x80000000) == 0 ) {
+    /* class A address */
+    nm = 0xFF000000;
+  } else if ( (ad & 0xC0000000) == 0x80000000 ) {
+    /* class B address */
+    nm = 0xFFFF0000;
+  } else if ( (ad & 0xE0000000) == 0xC0000000 ) {
+    /* class C address */
+    nm = 0xFFFFFF00;
+  }  else {
+    /* class D or E; netmask doesn't make much sense - guess 4 bits */
+    nm =  0xFFFFFFF0;
+  }
+  inm->s_addr = htonl(nm);
+}
+
+/****************************************************************************
+  get the broadcast address for our address 
+(troyer@saifr00.ateng.az.honeywell.com)
+****************************************************************************/
+void get_broadcast(struct in_addr *if_ipaddr,
+                    struct in_addr *if_bcast,
+                    struct in_addr *if_nmask)
+{  
+  BOOL found = False;
+#ifndef NO_GET_BROADCAST
+  int sock = -1;               /* AF_INET raw socket desc */
+  char buff[1024];
+  struct ifreq *ifr=NULL;
+  int i;
+
+#if defined(EVEREST)
+  int n_interfaces;
+  struct ifconf ifc;
+  struct ifreq  *ifreqs;
+#elif defined(USE_IFREQ)
+  struct ifreq ifreq;
+  struct strioctl strioctl;
+  struct ifconf *ifc;
+#else
+  struct ifconf ifc;
+#endif
+#endif
+
+  /* get a default netmask and broadcast */
+  default_netmask(if_nmask, if_ipaddr);
+
+#ifndef NO_GET_BROADCAST  
+  /* Create a socket to the INET kernel. */
+#if USE_SOCKRAW
+  if ((sock = socket(AF_INET, SOCK_RAW, PF_INET )) < 0)
+#else
+    if ((sock = socket(AF_INET, SOCK_DGRAM, 0 )) < 0)
+#endif
+      {
+        DEBUG(0,( "Unable to open socket to get broadcast address\n"));
+        return;
+      }
+  
+  /* Get a list of the configured interfaces */
+#ifdef EVEREST
+  /* This is part of SCO Openserver 5: The ioctls are no longer part
+     if the lower level STREAMS interface glue. They are now real
+     ioctl calls */
+
+  if (ioctl(sock, SIOCGIFANUM, &n_interfaces) < 0) {
+    DEBUG(0,( "SIOCGIFANUM: %s\n", strerror(errno)));
+  } else {
+    DEBUG(0,( "number of interfaces returned is: %d\n", n_interfaces));
+
+    ifc.ifc_len = sizeof(struct ifreq) * n_interfaces;
+    ifc.ifc_buf = (caddr_t) alloca(ifc.ifc_len);
+
+    if (ioctl(sock, SIOCGIFCONF, &ifc) < 0)
+      DEBUG(0, ( "SIOCGIFCONF: %s\n", strerror(errno)));
+    else {
+      ifr = ifc.ifc_req;
+
+      for (i = 0; i < n_interfaces; ++i) {
+       if (if_ipaddr->s_addr ==
+           ((struct sockaddr_in *) &ifr[i].ifr_addr)->sin_addr.s_addr) {
+         found = True;
+         break;
+       }
+      }
+    }
+  }
+#elif defined(USE_IFREQ)
+  ifc = (struct ifconf *)buff;
+  ifc->ifc_len = BUFSIZ - sizeof(struct ifconf);
+  strioctl.ic_cmd = SIOCGIFCONF;
+  strioctl.ic_dp  = (char *)ifc;
+  strioctl.ic_len = sizeof(buff);
+  if (ioctl(sock, I_STR, &strioctl) < 0) {
+    DEBUG(0,( "I_STR/SIOCGIFCONF: %s\n", strerror(errno)));
+  } else {
+    ifr = (struct ifreq *)ifc->ifc_req;  
+
+    /* Loop through interfaces, looking for given IP address */
+    for (i = ifc->ifc_len / sizeof(struct ifreq); --i >= 0; ifr++) {
+      if (if_ipaddr->s_addr ==
+         (*(struct sockaddr_in *) &ifr->ifr_addr).sin_addr.s_addr) {
+       found = True;
+       break;
+      }
+    }
+  }
+#elif defined(__FreeBSD__) || defined(NETBSD)
+  ifc.ifc_len = sizeof(buff);
+  ifc.ifc_buf = buff;
+  if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) {
+    DEBUG(0,("SIOCGIFCONF: %s\n", strerror(errno)));
+  } else {
+    ifr = ifc.ifc_req;
+    /* Loop through interfaces, looking for given IP address */
+    i = ifc.ifc_len;
+    while (i > 0) {
+      if (if_ipaddr->s_addr ==
+         (*(struct sockaddr_in *) &ifr->ifr_addr).sin_addr.s_addr) {
+       found = True;
+       break;
+      }
+      i -= ifr->ifr_addr.sa_len + IFNAMSIZ;
+      ifr = (struct ifreq*) ((char*) ifr + ifr->ifr_addr.sa_len + IFNAMSIZ);
+    }
+  }
+#else
+  ifc.ifc_len = sizeof(buff);
+  ifc.ifc_buf = buff;
+  if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) {
+    DEBUG(0,("SIOCGIFCONF: %s\n", strerror(errno)));
+  } else {
+    ifr = ifc.ifc_req;
+  
+    /* Loop through interfaces, looking for given IP address */
+    for (i = ifc.ifc_len / sizeof(struct ifreq); --i >= 0; ifr++) {
+#ifdef BSDI
+      if (ioctl(sock, SIOCGIFADDR, ifr) < 0) break;
+#endif
+      if (if_ipaddr->s_addr ==
+         (*(struct sockaddr_in *) &ifr->ifr_addr).sin_addr.s_addr) {
+       found = True;
+       break;
+      }
+    }
+  }
+#endif
+  
+  if (!found) {
+    DEBUG(0,("No interface found for address %s\n", inet_ntoa(*if_ipaddr)));
+  } else {
+    /* Get the netmask address from the kernel */
+#ifdef USE_IFREQ
+    ifreq = *ifr;
+  
+    strioctl.ic_cmd = SIOCGIFNETMASK;
+    strioctl.ic_dp  = (char *)&ifreq;
+    strioctl.ic_len = sizeof(struct ifreq);
+    if (ioctl(sock, I_STR, &strioctl) < 0)
+      DEBUG(0,("Failed I_STR/SIOCGIFNETMASK: %s\n", strerror(errno)));
+    else
+      *if_nmask = ((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr;
+#else
+    if (ioctl(sock, SIOCGIFNETMASK, ifr) < 0)
+      DEBUG(0,("SIOCGIFNETMASK failed\n"));
+    else
+      *if_nmask = ((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr;
+#endif
+
+    DEBUG(2,("Netmask for %s = %s\n", ifr->ifr_name,
+            inet_ntoa(*if_nmask)));
+  }
+
+  /* Close up shop */
+  (void) close(sock);
+  
+#endif
+
+  /* sanity check on the netmask */
+  {
+    unsigned long nm = ntohl(if_nmask->s_addr);
+    if ((nm >> 24) != 0xFF) {
+      DEBUG(0,("Impossible netmask %s - using defaults\n",inet_ntoa(*if_nmask)));
+      default_netmask(if_nmask, if_ipaddr);      
+    }
+  }
+
+  /* derive the broadcast assuming a 1's broadcast, as this is what
+     all MS operating systems do, we have to comply even if the unix
+     box is setup differently */
+  {
+    unsigned long ad = ntohl(if_ipaddr->s_addr);
+    unsigned long nm = ntohl(if_nmask->s_addr);
+    unsigned long bc = (ad & nm) | (0xffffffff & ~nm);
+    if_bcast->s_addr = htonl(bc);
+  }
+  
+  DEBUG(2,("Derived broadcast address %s\n", inet_ntoa(*if_bcast)));
+}  /* get_broadcast */
+
+
+/****************************************************************************
+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);
+}
+
+/****************************************************************************
+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,FILE *f)
+{
+  char *s=s2;
+  int len = 0;
+  int c;
+  BOOL start_of_line = True;
+
+  if (feof(f))
+    return(NULL);
+
+  if (!s2)
+    {
+      maxlen = MIN(maxlen,8);
+      s = (char *)Realloc(s,maxlen);
+    }
+
+  if (!s || maxlen < 2) return(NULL);
+
+  *s = 0;
+
+  while (len < maxlen-1)
+    {
+      c = 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) 
+           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)
+       {
+         maxlen *= 2;
+         s = (char *)Realloc(s,maxlen);
+         if (!s) return(NULL);
+       }
+    }
+  return(s);
+}
+
+
+
+/****************************************************************************
+set the length of a file from a filedescriptor.
+Returns 0 on success, -1 on failure.
+****************************************************************************/
+int set_filelen(int fd, long len)
+{
+/* According to W. R. Stevens advanced UNIX prog. Pure 4.3 BSD cannot
+   extend a file with ftruncate. Provide alternate implementation
+   for this */
+
+#if FTRUNCATE_CAN_EXTEND
+  return ftruncate(fd, len);
+#else
+  struct stat st;
+  char c = 0;
+  long currpos = lseek(fd, 0L, SEEK_CUR);
+
+  if(currpos < 0)
+    return -1;
+  /* Do an fstat to see if the file is longer than
+     the requested size (call ftruncate),
+     or shorter, in which case seek to len - 1 and write 1
+     byte of zero */
+  if(fstat(fd, &st)<0)
+    return -1;
+
+#ifdef S_ISFIFO
+  if (S_ISFIFO(st.st_mode)) return 0;
+#endif
+
+  if(st.st_size == len)
+    return 0;
+  if(st.st_size > len)
+    return ftruncate(fd, len);
+
+  if(lseek(fd, len-1, SEEK_SET) != len -1)
+    return -1;
+  if(write(fd, &c, 1)!=1)
+    return -1;
+  /* Seek to where we were */
+  lseek(fd, currpos, SEEK_SET);
+  return 0;
+#endif
+}
+
+
+/****************************************************************************
+return the byte checksum of some data
+****************************************************************************/
+int byte_checksum(char *buf,int len)
+{
+  unsigned char *p = (unsigned char *)buf;
+  int ret = 0;
+  while (len--)
+    ret += *p++;
+  return(ret);
+}
+
+
+
+#ifdef HPUX
+/****************************************************************************
+this is a version of setbuffer() for those machines that only have setvbuf
+****************************************************************************/
+void setbuffer(FILE *f,char *buf,int bufsize)
+{
+  setvbuf(f,buf,_IOFBF,bufsize);
+}
+#endif
+
+
+/****************************************************************************
+parse out a directory name from a path name. Assumes dos style filenames.
+****************************************************************************/
+char *dirname_dos(char *path,char *buf)
+{
+  char *p = strrchr(path,'\\');
+
+  if (!p)
+    strcpy(buf,path);
+  else
+    {
+      *p = 0;
+      strcpy(buf,path);
+      *p = '\\';
+    }
+
+  return(buf);
+}
+
+
+/****************************************************************************
+parse out a filename from a path name. Assumes dos style filenames.
+****************************************************************************/
+static char *filename_dos(char *path,char *buf)
+{
+  char *p = strrchr(path,'\\');
+
+  if (!p)
+    strcpy(buf,path);
+  else
+    strcpy(buf,p+1);
+
+  return(buf);
+}
+
+
+
+/****************************************************************************
+expand a pointer to be a particular size
+****************************************************************************/
+void *Realloc(void *p,int size)
+{
+  void *ret=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",size));
+
+  return(ret);
+}
+
+/****************************************************************************
+set the time on a file
+****************************************************************************/
+BOOL set_filetime(char *fname,time_t mtime)
+{  
+  struct utimbuf times;
+
+  if (null_mtime(mtime)) return(True);
+
+  times.modtime = times.actime = mtime;
+
+  if (sys_utime(fname,&times)) {
+    DEBUG(4,("set_filetime(%s) failed: %s\n",fname,strerror(errno)));
+  }
+    
+  return(True);
+}
+
+
+#ifdef NOSTRDUP
+/****************************************************************************
+duplicate a string
+****************************************************************************/
+char *strdup(char *s)
+{
+  char *ret = NULL;
+  if (!s) return(NULL);
+  ret = (char *)malloc(strlen(s)+1);
+  if (!ret) return(NULL);
+  strcpy(ret,s);
+  return(ret);
+}
+#endif
+
+
+/****************************************************************************
+  Signal handler for SIGPIPE (write on a disconnected socket) 
+****************************************************************************/
+void Abort(void )
+{
+  DEBUG(0,("Probably got SIGPIPE\nExiting\n"));
+  exit(2);
+}
+
+
+#ifdef REPLACE_STRLEN
+/****************************************************************************
+a replacement strlen() that returns int for solaris
+****************************************************************************/
+int Strlen(char *s)
+{
+  int ret=0;
+  if (!s) return(0);
+  while (*s++) ret++;
+  return(ret);
+}
+#endif
+
+
+/****************************************************************************
+return a time at the start of the current month
+****************************************************************************/
+time_t start_of_month(void)
+{
+  time_t t = time(NULL);
+  struct tm *t2;
+  
+  t2 = gmtime(&t);
+  
+  t2->tm_mday = 1;
+  t2->tm_hour = 0;
+  t2->tm_min = 0;
+  t2->tm_sec = 0;
+  
+  return(mktime(t2));
+}
+
+
+/*******************************************************************
+  check for a sane unix date
+********************************************************************/
+BOOL sane_unix_date(time_t unixdate)
+{
+  struct tm t,today;
+  time_t t_today = time(NULL);
+  
+  t = *(LocalTime(&unixdate,LOCAL_TO_GMT));
+  today = *(LocalTime(&t_today,LOCAL_TO_GMT));
+  
+  if (t.tm_year < 80)
+    return(False);
+  
+  if (t.tm_year >  today.tm_year)
+    return(False);
+  
+  if (t.tm_year == today.tm_year &&
+      t.tm_mon > today.tm_mon)
+    return(False);
+  
+  
+  if (t.tm_year == today.tm_year &&
+      t.tm_mon == today.tm_mon &&
+      t.tm_mday > (today.tm_mday+1))
+    return(False);
+  
+  return(True);
+}
+
+
+
+#ifdef NO_FTRUNCATE
+ /*******************************************************************
+ftruncate for operating systems that don't have it
+********************************************************************/
+int ftruncate(int f,long 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
+
+
+
+/****************************************************************************
+get my own name and IP
+****************************************************************************/
+BOOL get_myname(char *myname,struct in_addr *ip)
+{
+  struct hostent *hp;
+  pstring hostname;
+
+  *hostname = 0;
+
+  /* get my host name */
+  if (gethostname(hostname, MAXHOSTNAMELEN) == -1) 
+    {
+      DEBUG(0,("gethostname failed\n"));
+      return False;
+    } 
+
+  /* get host info */
+  if ((hp = Get_Hostbyname(hostname)) == 0) 
+    {
+      DEBUG(0,( "Get_Hostbyname: Unknown host %s.\n",hostname));
+      return False;
+    }
+
+  if (myname)
+    {
+      /* split off any parts after an initial . */
+      char *p = strchr(hostname,'.');
+      if (p) *p = 0;
+
+      strcpy(myname,hostname);
+    }
+
+  if (ip)
+    putip((char *)ip,(char *)hp->h_addr);
+
+  return(True);
+}
+
+
+/****************************************************************************
+true if two IP addresses are equal
+****************************************************************************/
+BOOL ip_equal(struct in_addr ip1,struct in_addr ip2)
+{
+  unsigned long a1,a2;
+  a1 = ntohl(ip1.s_addr);
+  a2 = ntohl(ip2.s_addr);
+  return(a1 == a2);
+}
+
+
+/****************************************************************************
+open a socket of the specified type, port and address for incoming data
+****************************************************************************/
+int open_socket_in(int type, int port, int dlevel)
+{
+  struct hostent *hp;
+  struct sockaddr_in sock;
+  pstring host_name;
+  int res;
+
+  /* get my host name */
+#ifdef MAXHOSTNAMELEN
+  if (gethostname(host_name, MAXHOSTNAMELEN) == -1) 
+#else
+  if (gethostname(host_name, sizeof(host_name)) == -1) 
+#endif
+    { DEBUG(0,("gethostname failed\n")); return -1; } 
+
+  /* get host info */
+  if ((hp = Get_Hostbyname(host_name)) == 0) 
+    {
+      DEBUG(0,( "Get_Hostbyname: Unknown host. %s\n",host_name));
+      return -1;
+    }
+  
+  bzero((char *)&sock,sizeof(sock));
+  memcpy((char *)&sock.sin_addr,(char *)hp->h_addr, hp->h_length);
+#if defined(__FreeBSD__) || defined(NETBSD) /* XXX not the right ifdef */
+  sock.sin_len = sizeof(sock);
+#endif
+  sock.sin_port = htons( port );
+  sock.sin_family = hp->h_addrtype;
+  sock.sin_addr.s_addr = INADDR_ANY;
+  res = socket(hp->h_addrtype, type, 0);
+  if (res == -1) 
+    { DEBUG(0,("socket failed\n")); return -1; }
+
+  {
+    int one=1;
+    setsockopt(res,SOL_SOCKET,SO_REUSEADDR,(char *)&one,sizeof(one));
+  }
+
+  /* now we've got a socket - we need to bind it */
+  if (bind(res, (struct sockaddr * ) &sock,sizeof(sock)) < 0) 
+    { 
+      if (port) {
+       if (port == 139 || port == 137)
+         DEBUG(dlevel,("bind failed on port %d (%s)\n",
+                       port,strerror(errno))); 
+       close(res); 
+
+       if (dlevel > 0 && port < 1000)
+         port = 7999;
+
+       if (port >= 1000 && port < 9000)
+         return(open_socket_in(type,port+1,dlevel));
+      }
+
+      return(-1); 
+    }
+  DEBUG(3,("bind succeeded on port %d\n",port));
+
+  return res;
+}
+
+
+/****************************************************************************
+  create an outgoing socket
+  **************************************************************************/
+int open_socket_out(int type, struct in_addr *addr, int port )
+{
+  struct sockaddr_in sock_out;
+  int res;
+
+  /* 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);
+  
+  bzero((char *)&sock_out,sizeof(sock_out));
+  putip((char *)&sock_out.sin_addr,(char *)addr);
+  
+  sock_out.sin_port = htons( port );
+  sock_out.sin_family = PF_INET;
+
+  DEBUG(3,("Connecting to %s at port %d\n",inet_ntoa(*addr),port));
+  
+  /* and connect it to the destination */
+  if (connect(res,(struct sockaddr *)&sock_out,sizeof(sock_out))<0) {
+    DEBUG(0,("connect error: %s\n",strerror(errno))); 
+    close(res); 
+    return(-1);
+  }
+
+  return res;
+}
+
+
+/****************************************************************************
+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);
+}
+
+/****************************************************************************
+interpret a security level
+****************************************************************************/
+int interpret_security(char *str,int def)
+{
+  if (strequal(str,"SERVER"))
+    return(SEC_SERVER);
+  if (strequal(str,"USER"))
+    return(SEC_USER);
+  if (strequal(str,"SHARE"))
+    return(SEC_SHARE);
+  
+  DEBUG(0,("Unrecognised security level %s\n",str));
+  
+  return(def);
+}
+
+
+/****************************************************************************
+interpret an internet address or name into an IP address in 4 byte form
+****************************************************************************/
+unsigned long interpret_addr(char *str)
+{
+  struct hostent *hp;
+  unsigned long 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 (isdigit(str[0])) {
+    res = inet_addr(str);
+  } else {
+    /* otherwise assume it's a network name of some sort and use Get_Hostbyname */
+    if ((hp = Get_Hostbyname(str)) == 0) {
+      DEBUG(3,("Get_Hostbyname: Unknown host. %s\n",str));
+      return 0;
+    }
+    putip((char *)&res,(char *)hp->h_addr);
+  }
+
+  if (res == (unsigned long)-1) return(0);
+
+  return(res);
+}
+
+/*******************************************************************
+  a convenient addition to interpret_addr()
+  ******************************************************************/
+struct in_addr *interpret_addr2(char *str)
+{
+  static struct in_addr ret;
+  unsigned long a = interpret_addr(str);
+  putip((char *)&ret,(char *)&a);
+  return(&ret);
+}
+
+/*******************************************************************
+  check if an IP is the 0.0.0.0
+  ******************************************************************/
+BOOL zero_ip(struct in_addr ip)
+{
+  unsigned long a;
+  putip((char *)&a,(char *)&ip);
+  return(a == 0);
+}
+
+#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 interpret_long_date(char *p)
+{
+  double d;
+  time_t ret;
+  uint32 tlow,thigh;
+  tlow = IVAL(p,0);
+  thigh = IVAL(p,4);
+
+  if (thigh == 0) return(0);
+
+  d = ((double)thigh)*4.0*(double)(1<<30);
+  d += (tlow&0xFFF00000);
+  d *= 1.0e-7;
+  /* now adjust by 369 years to make the secs since 1970 */
+  d -= TIME_FIXUP_CONSTANT;
+
+  if (d>=MAXINT)
+    return(0);
+
+  ret = (time_t)(d+0.5);
+
+  /* this takes us from kludge-GMT to real GMT */
+  ret += TimeDiff(ret) - serverzone;
+
+  return(ret);
+}
+
+
+/****************************************************************************
+put a 8 byte filetime from a time_t
+This takes real GMT as input and converts to kludge-GMT
+****************************************************************************/
+void put_long_date(char *p,time_t t)
+{
+  uint32 tlow,thigh;
+  double d;
+
+  if (t==0) {
+    SIVAL(p,0,0); SIVAL(p,4,0);
+    return;
+  }
+
+  /* this converts GMT to kludge-GMT */
+  t -= TimeDiff(t) - serverzone; 
+
+  d = (double) (t);
+
+  d += TIME_FIXUP_CONSTANT;
+
+  d *= 1.0e7;
+
+  thigh = (uint32)(d * (1.0/(4.0*(double)(1<<30))));
+  tlow = (uint32)(d - ((double)thigh)*4.0*(double)(1<<30));
+
+  SIVAL(p,0,tlow);
+  SIVAL(p,4,thigh);
+}
+
+/*******************************************************************
+sub strings with useful parameters
+********************************************************************/
+void standard_sub_basic(char *s)
+{
+  if (!strchr(s,'%')) return;
+
+  string_sub(s,"%R",remote_proto);
+  string_sub(s,"%a",remote_arch);
+  string_sub(s,"%m",remote_machine);
+  string_sub(s,"%L",local_machine);
+
+  if (!strchr(s,'%')) return;
+
+  string_sub(s,"%v",VERSION);
+  string_sub(s,"%h",myhostname);
+  string_sub(s,"%U",sesssetup_user);
+
+  if (!strchr(s,'%')) return;
+
+  string_sub(s,"%I",Client_info.addr);
+  string_sub(s,"%M",Client_info.name);
+  string_sub(s,"%T",timestring());
+
+  if (!strchr(s,'%')) return;
+
+  {
+    char pidstr[10];
+    sprintf(pidstr,"%d",(int)getpid());
+    string_sub(s,"%d",pidstr);
+  }
+
+  if (!strchr(s,'%')) return;
+
+  {
+    struct passwd *pass = Get_Pwnam(sesssetup_user,False);
+    if (pass) {
+      string_sub(s,"%G",gidtoname(pass->pw_gid));
+    }
+  }
+}
+
+
+/*******************************************************************
+write a string in unicoode format
+********************************************************************/
+int PutUniCode(char *dst,char *src)
+{
+  int ret = 0;
+  while (*src) {
+    dst[ret++] = src[0];
+    dst[ret++] = 0;    
+    src++;
+  }
+  dst[ret++]=0;
+  dst[ret++]=0;
+  return(ret);
+}
+
+
+pstring smbrun_path = SMBRUN;
+
+/****************************************************************************
+run a command via system() using smbrun
+****************************************************************************/
+int smbrun(char *cmd,char *outfile)
+{
+  int ret;
+  pstring syscmd;  
+
+  if (!file_exist(smbrun_path,NULL))
+    {
+      DEBUG(0,("SMBRUN ERROR: Can't find %s. Installation problem?\n",smbrun_path));
+      return(1);
+    }
+
+  sprintf(syscmd,"%s \"(%s 2>&1) > %s\"",
+         smbrun_path,cmd,
+         outfile?outfile:"/dev/null");
+
+  DEBUG(5,("smbrun - running %s ",syscmd));
+  ret = system(syscmd);
+  DEBUG(5,("gave %d\n",ret));
+  return(ret);
+}
+
+
+/****************************************************************************
+a wrapper for gethostbyname() that tries with all lower and all upper case 
+if the initial name fails
+****************************************************************************/
+struct hostent *Get_Hostbyname(char *name)
+{
+  char *name2 = strdup(name);
+  struct hostent *ret;
+
+  if (!name2)
+    {
+      DEBUG(0,("Memory allocation error in Get_Hostbyname! panic\n"));
+      exit(0);
+    }
+
+  if (!isalnum(*name2))
+    {
+      free(name2);
+      return(NULL);
+    }
+
+  ret = gethostbyname(name2);
+  if (ret != NULL)
+    {
+      free(name2);
+      return(ret);
+    }
+
+  /* try with all lowercase */
+  strlower(name2);
+  ret = gethostbyname(name2);
+  if (ret != NULL)
+    {
+      free(name2);
+      return(ret);
+    }
+
+  /* try with all uppercase */
+  strupper(name2);
+  ret = gethostbyname(name2);
+  if (ret != NULL)
+    {
+      free(name2);
+      return(ret);
+    }
+  
+  /* nothing works :-( */
+  free(name2);
+  return(NULL);
+}
+
+
+/****************************************************************************
+check if a process exists. Does this work on all unixes?
+****************************************************************************/
+BOOL process_exists(int pid)
+{
+#ifdef LINUX
+  fstring s;
+  sprintf(s,"/proc/%d",pid);
+  return(directory_exist(s,NULL));
+#else
+  {
+    static BOOL tested=False;
+    static BOOL ok=False;
+    fstring s;
+    if (!tested) {
+      tested = True;
+      sprintf(s,"/proc/%05d",getpid());
+      ok = file_exist(s,NULL);
+    }
+    if (ok) {
+      sprintf(s,"/proc/%05d",pid);
+      return(file_exist(s,NULL));
+    }
+  }
+
+  /* a best guess for non root access */
+  if (geteuid() != 0) return(True);
+
+  /* otherwise use kill */
+  return(pid == getpid() || kill(pid,0) == 0);
+#endif
+}
+
+
+/*******************************************************************
+turn a uid into a user name
+********************************************************************/
+char *uidtoname(int uid)
+{
+  static char name[40];
+  struct passwd *pass = getpwuid(uid);
+  if (pass) return(pass->pw_name);
+  sprintf(name,"%d",uid);
+  return(name);
+}
+
+/*******************************************************************
+turn a gid into a group name
+********************************************************************/
+char *gidtoname(int gid)
+{
+  static char name[40];
+  struct group *grp = getgrgid(gid);
+  if (grp) return(grp->gr_name);
+  sprintf(name,"%d",gid);
+  return(name);
+}
+
+/*******************************************************************
+block sigs
+********************************************************************/
+void BlockSignals(BOOL block)
+{
+#ifdef USE_SIGBLOCK
+  int block_mask = (sigmask(SIGTERM)|sigmask(SIGQUIT)|sigmask(SIGSEGV)
+                   |sigmask(SIGCHLD)|sigmask(SIGQUIT)|sigmask(SIGBUS)|
+                   sigmask(SIGINT));
+  if (block) 
+    sigblock(block_mask);
+  else
+    sigunblock(block_mask);
+#endif
+}
+
+#if AJT
+/*******************************************************************
+my own panic function - not suitable for general use
+********************************************************************/
+void ajt_panic(void)
+{
+  pstring cmd = "/usr/bin/X11/xedit -display :0 /tmp/ERROR_FAULT &";
+  smbrun(cmd,NULL);
+}
+#endif
+
+#ifdef USE_DIRECT
+#define DIRECT direct
+#else
+#define DIRECT dirent
+#endif
+
+/*******************************************************************
+a readdir wrapper which just returns the file name
+also return the inode number if requested
+********************************************************************/
+char *readdirname(void *p)
+{
+  struct DIRECT *ptr;
+  char *dname;
+
+  if (!p) return(NULL);
+  
+  ptr = (struct DIRECT *)readdir(p);
+  if (!ptr) return(NULL);
+
+  dname = ptr->d_name;
+
+#ifdef KANJI
+  {
+    static pstring buf;
+    strcpy(buf, dname);
+    unix_to_dos(buf, True);
+    dname = buf;
+  }
+#endif
+
+#ifdef NEXT2
+  if (telldir(p) < 0) return(NULL);
+#endif
+
+#ifdef SUNOS5
+  /* this handles a broken compiler setup, causing a mixture
+   of BSD and SYSV headers and libraries */
+  {
+    static BOOL broken_readdir = False;
+    if (!broken_readdir && !(*(dname)) && strequal("..",dname-2))
+      {
+       DEBUG(0,("Your readdir() is broken. You have somehow mixed SYSV and BSD headers and libraries\n"));
+       broken_readdir = True;
+      }
+    if (broken_readdir)
+      return(dname-2);
+  }
+#endif
+
+  return(dname);
+}
+
+
+
+#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
+
+#ifdef NO_INITGROUPS
+#include <sys/types.h>
+#include <limits.h>
+#include <grp.h>
+
+#ifndef NULL
+#define NULL (void *)0
+#endif
+
+/****************************************************************************
+ some systems don't have an initgroups call 
+****************************************************************************/
+int initgroups(char *name,gid_t id)
+{
+#ifdef NO_SETGROUPS
+  /* yikes! no SETGROUPS or INITGROUPS? how can this work? */
+  return(0);
+#else
+  gid_t  grouplst[NGROUPS_MAX];
+  int    i,j;
+  struct group *g;
+  char   *gr;
+
+  grouplst[0] = id;
+  i = 1;
+  while (i < NGROUPS_MAX && 
+        ((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();
+  return(setgroups(i,grouplst));
+#endif
+}
+#endif
+
+
+#if WRAP_MALLOC
+
+/* undo the wrapping temporarily */
+#undef malloc
+#undef realloc
+#undef free
+
+/****************************************************************************
+wrapper for malloc() to catch memory errors
+****************************************************************************/
+void *malloc_wrapped(int size,char *file,int line)
+{
+#ifdef xx_old_malloc
+  void *res = xx_old_malloc(size);
+#else
+  void *res = malloc(size);
+#endif
+  DEBUG(3,("Malloc called from %s(%d) with size=%d gave ptr=0x%X\n",
+       file,line,
+       size,(unsigned int)res));
+  return(res);
+}
+
+/****************************************************************************
+wrapper for realloc() to catch memory errors
+****************************************************************************/
+void *realloc_wrapped(void *ptr,int size,char *file,int line)
+{
+#ifdef xx_old_realloc
+  void *res = xx_old_realloc(ptr,size);
+#else
+  void *res = realloc(ptr,size);
+#endif
+  DEBUG(3,("Realloc\n"));
+  DEBUG(3,("free called from %s(%d) with ptr=0x%X\n",
+       file,line,
+       (unsigned int)ptr));
+  DEBUG(3,("Malloc called from %s(%d) with size=%d gave ptr=0x%X\n",
+       file,line,
+       size,(unsigned int)res));
+  return(res);
+}
+
+/****************************************************************************
+wrapper for free() to catch memory errors
+****************************************************************************/
+void free_wrapped(void *ptr,char *file,int line)
+{
+#ifdef xx_old_free
+  xx_old_free(ptr);
+#else
+  free(ptr);
+#endif
+  DEBUG(3,("free called from %s(%d) with ptr=0x%X\n",
+       file,line,(unsigned int)ptr));
+  return;
+}
+
+/* and re-do the define for spots lower in this file */
+#define malloc(size) malloc_wrapped(size,__FILE__,__LINE__)
+#define realloc(ptr,size) realloc_wrapped(ptr,size,__FILE__,__LINE__)
+#define free(ptr) free_wrapped(ptr,__FILE__,__LINE__)
+
+#endif
+
+#ifdef REPLACE_STRSTR
+/****************************************************************************
+Mips version of strstr doesn't seem to work correctly.
+There is a #define in includes.h to redirect calls to this function.
+****************************************************************************/
+char *Strstr(char *s, char *p)
+{
+       int len = strlen(p);
+
+       while ( *s != '\0' ) {
+               if ( strncmp(s, p, len) == 0 )
+               return s;
+               s++;
+       }
+
+       return NULL;
+}
+#endif /* REPLACE_STRSTR */
+
+
+#ifdef REPLACE_MKTIME
+/*******************************************************************
+a mktime() replacement for those who don't have it - contributed by 
+C.A. Lademann <cal@zls.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             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);
+
+  epoch = (t->tm_year - 70) * YEAR + 
+    (t->tm_year / 4 - 70 / 4 - t->tm_year / 100) * DAY;
+
+  y = t->tm_year;
+  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;
+#ifndef NO_TM_NAME
+    memcpy(t->tm_name, u->tm_name, LTZNMAX);
+#endif
+  }
+
+  return(epoch);
+}
+#endif /* REPLACE_MKTIME */
+
+
+
+#ifdef REPLACE_RENAME
+/* Rename a file. (from libiberty in GNU binutils)  */
+int
+rename (zfrom, zto)
+     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
+
+
+#ifdef REPLACE_INNETGR
+/*
+ * Search for a match in a netgroup. This replaces it on broken systems.
+ */
+int InNetGr(group, host, user, dom)
+        char *group, *host, *user, *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
+
+
+#if WRAP_MEMCPY
+#undef memcpy
+/*******************************************************************
+a wrapper around memcpy for diagnostic purposes
+********************************************************************/
+void *memcpy_wrapped(void *d,void *s,int l,char *fname,int line)
+{
+  if (l>64 && (((int)d)%4) != (((int)s)%4))
+    DEBUG(4,("Misaligned memcpy(0x%X,0x%X,%d) at %s(%d)\n",d,s,l,fname,line));
+#ifdef xx_old_memcpy  
+  return(xx_old_memcpy(d,s,l));
+#else
+  return(memcpy(d,s,l));
+#endif
+}
+#define memcpy(d,s,l) memcpy_wrapped(d,s,l,__FILE__,__LINE__)
+#endif
+
+
+
diff --git a/source/libsmb/nmblib.c b/source/libsmb/nmblib.c
new file mode 100644 (file)
index 0000000..6743227
--- /dev/null
@@ -0,0 +1,936 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   NBT netbios library routines
+   Copyright (C) Andrew Tridgell 1994-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"
+#include "nameserv.h"
+
+extern int DEBUGLEVEL;
+
+int num_good_sends=0;
+int  num_good_receives=0;
+static uint16 name_trn_id = 0;
+BOOL CanRecurse = True;
+extern pstring scope;
+
+/*******************************************************************
+  handle "compressed" name pointers
+  ******************************************************************/
+static BOOL handle_name_ptrs(unsigned char *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 offset,int length,
+                         struct nmb_name *name)
+{
+  int m,n=0;
+  unsigned char *ubuf = (unsigned char *)inbuf;
+  int ret = 0;
+  BOOL got_pointer=False;
+
+  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);
+
+  bzero((char *)name,sizeof(*name));
+
+  /* the "compressed" part */
+  if (!got_pointer) ret += m + 2;
+  offset++;
+  while (m) {
+    unsigned char c1,c2;
+    c1 = ubuf[offset++]-'A';
+    c2 = ubuf[offset++]-'A';
+    if ((c1 & 0xF0) || (c2 & 0xF0)) 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 = name->name[15];
+  
+    /* 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 ((m=ubuf[offset])) {
+    /* we can have pointers within the domain part as well */
+    if (!handle_name_ptrs(ubuf,&offset,length,&got_pointer,&ret)) 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++];
+  }
+  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 (name->name[0] == '*') {
+    /* special case for wildcard name */
+    bzero(buf1,20);
+    buf1[0] = '*';
+  } else {
+    sprintf(buf1,"%-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;
+    strcpy(&buf[offset+1],name->scope);  
+  
+    p = &buf[offset+1];
+    while ((p = strchr(p,'.'))) {
+      buf[offset] = PTR_DIFF(p,&buf[offset]);
+      offset += buf[offset];
+      p = &buf[offset+1];
+    }
+    buf[offset] = strlen(&buf[offset+1]);
+  }
+
+  return(ret);
+}
+
+/*******************************************************************
+  useful for debugging messages
+  ******************************************************************/
+char *namestr(struct nmb_name *n)
+{
+  static int i=0;
+  static fstring ret[4];
+  char *p = ret[i];
+
+  if (!n->scope[0])
+    sprintf(p,"%s(%x)",n->name,n->name_type);
+  else
+    sprintf(p,"%s(%x).%s",n->name,n->name_type,n->scope);
+
+  i = (i+1)%4;
+  return(p);
+}
+
+/*******************************************************************
+  allocate are 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);
+
+  bzero(*recs,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) {
+      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) {
+      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);
+}
+
+/*******************************************************************
+  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;
+
+  bzero((char *)dgram,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;
+
+  bzero((char *)nmb,sizeof(*nmb));
+
+  if (length < 12) return(False);
+
+  /* parse the header */
+  nmb->header.name_trn_id = RSVAL(inbuf,0);
+  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);
+}
+
+/*******************************************************************
+  free up any resources associated with an nmb packet
+  ******************************************************************/
+void free_nmb_packet(struct nmb_packet *nmb)
+{  
+  if (nmb->answers) free(nmb->answers);
+  if (nmb->nsrecs) free(nmb->nsrecs);
+  if (nmb->additional) free(nmb->additional);
+}
+
+/*******************************************************************
+  free up any resources associated with a packet
+  ******************************************************************/
+void free_packet(struct packet_struct *packet)
+{  
+  if (packet->packet_type == NMB_PACKET)
+    free_nmb_packet(&packet->packet.nmb);
+  free(packet);
+}
+
+/*******************************************************************
+  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)
+{
+  extern struct in_addr lastip;
+  extern int lastport;
+  struct packet_struct *packet;
+  char buf[MAX_DGRAM_SIZE];
+  int length;
+  BOOL ok=False;
+
+  length = read_udp_socket(fd,buf,sizeof(buf));
+  if (length < MIN_DGRAM_SIZE) return(NULL);
+
+  packet = (struct packet_struct *)malloc(sizeof(*packet));
+  if (!packet) return(NULL);
+
+  packet->next = NULL;
+  packet->prev = NULL;
+  packet->ip = lastip;
+  packet->port = lastport;
+  packet->fd = fd;
+  packet->timestamp = time(NULL);
+  packet->packet_type = packet_type;
+  switch (packet_type) 
+    {
+    case NMB_PACKET:
+      ok = parse_nmb(buf,length,&packet->packet.nmb);
+      break;
+
+    case DGRAM_PACKET:
+      ok = parse_dgram(buf,length,&packet->packet.dgram);
+      break;
+    }
+  if (!ok) {
+    free(packet);
+    return(NULL);
+  }
+
+  num_good_receives++;
+
+  DEBUG(4,("%s received a packet of len %d from (%s) port %d\n",
+          timestring(),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;
+  struct sockaddr_in sock_out;
+
+  /* set the address and port */
+  bzero((char *)&sock_out,sizeof(sock_out));
+  putip((char *)&sock_out.sin_addr,(char *)&ip);
+  sock_out.sin_port = htons( port );
+  sock_out.sin_family = AF_INET;
+  
+  DEBUG(4,("%s sending a packet of len %d to (%s) on port %d\n",
+          timestring(),len,inet_ntoa(ip),port));
+       
+  ret = (sendto(fd,buf,len,0,(struct sockaddr *)&sock_out,
+               sizeof(sock_out)) >= 0);
+
+  if (!ret)
+    DEBUG(0,("Packet send failed to %s(%d) ERRNO=%s\n",
+            inet_ntoa(ip),port,strerror(errno)));
+
+  if (ret)
+    num_good_sends++;
+
+  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.
+  ******************************************************************/
+static int build_dgram(char *buf,struct packet_struct *p)
+{
+  struct dgram_packet *dgram = &p->packet.dgram;
+  unsigned char *ubuf = (unsigned char *)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 */
+  dgram->header.dgm_length = offset;
+  RSSVAL(ubuf,10,dgram->header.dgm_length); 
+
+  return(offset);
+}
+
+/*******************************************************************
+  build a nmb name
+  ******************************************************************/
+void make_nmb_name(struct nmb_name *n,char *name,int type,char *this_scope)
+{
+  strcpy(n->name,name);
+  strupper(n->name);
+  n->name_type = type;
+  strcpy(n->scope,this_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;
+  unsigned char *ubuf = (unsigned char *)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) 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) 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);
+
+  if (nmb->header.arcount)
+    offset += put_res_rec((char *)ubuf,offset,nmb->additional,
+                         nmb->header.arcount);  
+
+  return(offset);
+}
+
+
+/*******************************************************************
+  send a packet_struct
+  ******************************************************************/
+BOOL send_packet(struct packet_struct *p)
+{
+  char buf[1024];
+  int len=0;
+
+  bzero(buf,sizeof(buf));
+
+  switch (p->packet_type) 
+    {
+    case NMB_PACKET:
+      len = build_nmb(buf,p);
+      break;
+
+    case DGRAM_PACKET:
+      len = build_dgram(buf,p);
+      break;
+    }
+
+  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;
+
+  FD_ZERO(&fds);
+  FD_SET(fd,&fds);
+  timeout.tv_sec = t/1000;
+  timeout.tv_usec = 1000*(t%1000);
+
+  sys_select(&fds,&timeout);
+
+  if (FD_ISSET(fd,&fds)) 
+    return(read_packet(fd,type));
+
+  return(NULL);
+}
+
+
+/****************************************************************************
+interpret a node status response
+****************************************************************************/
+static void interpret_node_status(char *p, char *master,char *rname)
+{
+  int level = (master||rname)?4:0;
+  int numnames = CVAL(p,0);
+  DEBUG(level,("received %d names\n",numnames));
+
+  if (rname) *rname = 0;
+  if (master) *master = 0;
+
+  p += 1;
+  while (numnames--)
+    {
+      char qname[17];
+      int type;
+      fstring flags;
+      *flags = 0;
+      StrnCpy(qname,p,15);
+      type = CVAL(p,15);
+      p += 16;
+
+      if (p[0] & 0x80) strcat(flags,"<GROUP> ");
+      if ((p[0] & 0x60) == 0) strcat(flags,"B ");
+      if ((p[0] & 0x60) == 1) strcat(flags,"P ");
+      if ((p[0] & 0x60) == 2) strcat(flags,"M ");
+      if ((p[0] & 0x60) == 3) strcat(flags,"_ ");
+      if (p[0] & 0x10) strcat(flags,"<DEREGISTERING> ");
+      if (p[0] & 0x08) strcat(flags,"<CONFLICT> ");
+      if (p[0] & 0x04) strcat(flags,"<ACTIVE> ");
+      if (p[0] & 0x02) strcat(flags,"<PERMANENT> ");
+
+      if (master && !*master && type == 0x1d) {
+       StrnCpy(master,qname,15);
+       trim_string(master,NULL," ");
+      }
+
+      if (rname && !*rname && type == 0x20 && !(p[0]&0x80)) {
+       StrnCpy(rname,qname,15);
+       trim_string(rname,NULL," ");
+      }
+      
+      DEBUG(level,("\t%s (type=0x%x)\t%s\n",qname,type,flags));
+      p+=2;
+    }
+  DEBUG(level,("num_good_sends=%d num_good_receives=%d\n",
+              IVAL(p,20),IVAL(p,24)));
+}
+
+
+/****************************************************************************
+  do a netbios name status query on a host
+
+  the "master" parameter is a hack used for finding workgroups.
+  **************************************************************************/
+BOOL name_status(int fd,char *name,int name_type,BOOL recurse,
+                struct in_addr to_ip,char *master,char *rname,
+                void (*fn)())
+{
+  BOOL found=False;
+  int retries = 2;
+  int retry_time = 5000;
+  struct timeval tval;
+  struct packet_struct p;
+  struct packet_struct *p2;
+  struct nmb_packet *nmb = &p.packet.nmb;
+
+  bzero((char *)&p,sizeof(p));
+
+  if (!name_trn_id) name_trn_id = (time(NULL)%(unsigned)0x7FFF) + 
+    (getpid()%(unsigned)100);
+  name_trn_id = (name_trn_id+1) % (unsigned)0x7FFF;
+
+  nmb->header.name_trn_id = name_trn_id;
+  nmb->header.opcode = 0;
+  nmb->header.response = False;
+  nmb->header.nm_flags.bcast = False;
+  nmb->header.nm_flags.recursion_available = CanRecurse;
+  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,scope);
+
+  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(False);
+
+  retries--;
+
+  while (1)
+    {
+      struct timeval tval2;
+      GetTimeOfDay(&tval2);
+      if (TvalDiff(&tval,&tval2) > retry_time) {
+       if (!retries) break;
+       if (!found && !send_packet(&p))
+         return False;
+       GetTimeOfDay(&tval);
+       retries--;
+      }
+
+      if ((p2=receive_packet(fd,NMB_PACKET,90)))
+       {     
+         struct nmb_packet *nmb2 = &p2->packet.nmb;
+         if (nmb->header.name_trn_id != nmb2->header.name_trn_id ||
+             !nmb2->header.response) {
+           /* its not for us - maybe deal with it later */
+           if (fn) 
+             fn(p2);
+           else
+             free_packet(p2);
+           continue;
+         }
+         
+         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;
+         }
+
+         interpret_node_status(&nmb2->answers->rdata[0], master,rname);
+         free_packet(p2);
+         return(True);
+       }
+    }
+  
+
+  DEBUG(0,("No status response (this is not unusual)\n"));
+
+  return(False);
+}
+
+
+/****************************************************************************
+  do a netbios name query to find someones IP
+  ****************************************************************************/
+BOOL name_query(int fd,char *name,int name_type, 
+               BOOL bcast,BOOL recurse,
+               struct in_addr to_ip, struct in_addr *ip,void (*fn)())
+{
+  BOOL found=False;
+  int 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;
+
+  bzero((char *)&p,sizeof(p));
+
+  if (!name_trn_id) name_trn_id = (time(NULL)%(unsigned)0x7FFF) + 
+    (getpid()%(unsigned)100);
+  name_trn_id = (name_trn_id+1) % (unsigned)0x7FFF;
+
+  nmb->header.name_trn_id = name_trn_id;
+  nmb->header.opcode = 0;
+  nmb->header.response = False;
+  nmb->header.nm_flags.bcast = bcast;
+  nmb->header.nm_flags.recursion_available = CanRecurse;
+  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,scope);
+
+  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(False);
+
+  retries--;
+
+  while (1)
+    {
+      struct timeval tval2;
+      GetTimeOfDay(&tval2);
+      if (TvalDiff(&tval,&tval2) > retry_time) {
+       if (!retries) break;
+       if (!found && !send_packet(&p))
+         return False;
+       GetTimeOfDay(&tval);
+       retries--;
+      }
+
+      if ((p2=receive_packet(fd,NMB_PACKET,90)))
+       {     
+         struct nmb_packet *nmb2 = &p2->packet.nmb;
+         if (nmb->header.name_trn_id != nmb2->header.name_trn_id ||
+             !nmb2->header.response) {
+           /* its not for us - maybe deal with it later 
+              (put it on the queue?) */
+           if (fn) 
+             fn(p2);
+           else
+             free_packet(p2);
+           continue;
+         }
+         
+         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;
+         }
+
+         if (ip) {
+           putip((char *)ip,&nmb2->answers->rdata[2]);
+           DEBUG(fn?3:2,("Got a positive name query response from %s",
+                         inet_ntoa(p2->ip)));
+           DEBUG(fn?3:2,(" (%s)\n",inet_ntoa(*ip)));
+         }
+         found=True; retries=0;
+         free_packet(p2);
+         if (fn) break;
+       }
+    }
+
+  return(found);
+}
+
+
+/****************************************************************************
+  construct and send a netbios DGRAM
+
+  Note that this currently sends all answers to port 138. thats the
+  wrong things to do! I should send to the requestors port. XXX
+  **************************************************************************/
+BOOL send_mailslot_reply(char *mailslot,int fd,char *buf,int len,
+                        char *srcname,char *dstname,
+                        int src_type,int dest_type,
+                        struct in_addr dest_ip,
+                        struct in_addr src_ip)
+{
+  struct packet_struct p;
+  struct dgram_packet *dgram = &p.packet.dgram;
+  char *ptr,*p2;
+  char tmp[4];
+
+  bzero((char *)&p,sizeof(p));
+
+  dgram->header.msg_type = 0x11; /* DIRECT GROUP DATAGRAM */
+  dgram->header.flags.node_type = M_NODE;
+  dgram->header.flags.first = True;
+  dgram->header.flags.more = False;
+  dgram->header.dgm_id = 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,scope);
+  make_nmb_name(&dgram->dest_name,dstname,dest_type,scope);
+
+  ptr = &dgram->data[0];
+
+  /* now 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);
+  strcpy(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 = DGRAM_PORT;
+  p.fd = fd;
+  p.timestamp = time(NULL);
+  p.packet_type = DGRAM_PACKET;
+
+  return(send_packet(&p));
+}
+
+
diff --git a/source/libsmb/smbencrypt.c b/source/libsmb/smbencrypt.c
new file mode 100644 (file)
index 0000000..a0683b5
--- /dev/null
@@ -0,0 +1,202 @@
+#ifdef SMB_PASSWD
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   SMB parameters and setup
+   Copyright (C) Andrew Tridgell 1992-1995
+   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"
+#include "loadparm.h"
+#include "des.h"
+#include "md4.h"
+
+extern int DEBUGLEVEL;
+
+#ifndef uchar
+#define uchar unsigned char
+#endif
+#ifndef int16
+#define int16 unsigned short
+#endif
+#ifndef uint16
+#define uint16 unsigned short
+#endif
+#ifndef uint32
+#define uint32 unsigned int
+#endif
+
+#include "byteorder.h"
+
+void str_to_key(uchar *str,uchar *key)
+{
+  void des_set_odd_parity(des_cblock *);
+  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);
+  }
+  des_set_odd_parity((des_cblock *)key);
+}
+
+void D1(uchar *k, uchar *d, uchar *out)
+{
+  des_key_schedule ks;
+  des_cblock deskey;
+
+  str_to_key(k,(uchar *)deskey);
+  des_set_key(deskey,ks);
+  des_ecb_encrypt(d, out, ks, DES_DECRYPT);
+}
+
+void E1(uchar *k, uchar *d, uchar *out)
+{
+  des_key_schedule ks;
+  des_cblock deskey;
+
+  str_to_key(k,(uchar *)deskey);
+  des_set_key(deskey,ks);
+  des_ecb_encrypt(d, out, ks, DES_ENCRYPT);
+}
+void E_P16(uchar *p14,uchar *p16)
+{
+  uchar sp7[7];
+  /* the following constant makes us compatible with other
+  implementations. Note that publishing this constant does not reduce the
+  security of the encryption mechanism */
+  uchar sp8[] = {0xAA,0xD3,0xB4,0x35,0xB5,0x14,0x4,0xEE};
+  uchar x[8];
+
+  memset(sp7,'\0',7);
+
+  D1(sp7, sp8, x);
+  E1(p14, x, p16);
+  E1(p14+7, x, p16+8);
+}
+
+void E_P24(uchar *p21, uchar *c8, uchar *p24)
+{
+  E1(p21, c8, p24);
+  E1(p21+7, c8, p24+8);
+  E1(p21+14, c8, p24+16);
+}
+
+
+/*
+   This implements the X/Open SMB password encryption
+   It takes a password, a 8 byte "crypt key" and puts 24 bytes of 
+   encrypted password into p24 */
+void SMBencrypt(uchar *passwd, uchar *c8, uchar *p24)
+{
+  uchar p14[15], p21[21];
+
+  memset(p21,'\0',21);
+  memset(p14,'\0',14);
+  StrnCpy((char *)p14,(char *)passwd,14);
+
+  strupper((char *)p14);
+  E_P16(p14, p21); 
+  E_P24(p21, c8, p24);
+}
+
+/* Routines for Windows NT MD4 Hash functions. */
+static int _my_wcslen(int16 *str)
+{
+       int len = 0;
+       while(*str++ != 0)
+               len++;
+       return len;
+}
+
+/*
+ * Convert a string into an NT UNICODE string.
+ * Note that regardless of processor type 
+ * this must be in intel (little-endian)
+ * format.
+ */
+static int _my_mbstowcs(int16 *dst, uchar *src, int len)
+{
+       int i;
+       int16 val;
+       for(i = 0; i < len; i++) {
+               val = *src;
+               SSVAL(dst,0,val);
+               dst++;
+               src++;
+               if(val == 0)
+                       break;
+       }
+       return i;
+}
+
+/* 
+ * Creates the MD4 Hash of the users password in NT UNICODE.
+ */
+void E_md4hash(uchar *passwd, uchar *p16)
+{
+       int i, len;
+       int16 wpwd[129];
+       MDstruct MD;
+       /* Password cannot be longer than 128 characters */
+       len = strlen(passwd);
+       if(len > 128)
+               len = 128;
+       /* Password must be converted to NT unicode */
+       _my_mbstowcs( wpwd, passwd, len);
+       wpwd[len] = 0; /* Ensure string is null terminated */
+       /* Calculate length in bytes */
+       len = _my_wcslen(wpwd) * sizeof(int16);
+       MDbegin(&MD);
+       for(i = 0; i + 64 <= len; i += 64)
+               MDupdate(&MD,wpwd + (i/2), 512);
+       MDupdate(&MD,wpwd + (i/2),(len-i)*8);
+       SIVAL(p16,0,MD.buffer[0]);
+       SIVAL(p16,4,MD.buffer[1]);
+       SIVAL(p16,8,MD.buffer[2]);
+       SIVAL(p16,12,MD.buffer[3]);
+}
+
+/* Does the NT MD4 hash then des encryption. */
+void SMBNTencrypt(uchar *passwd, uchar *c8, uchar *p24)
+{
+       uchar p21[21];
+       memset(p21,'\0',21);
+       E_md4hash(passwd, p21);    
+       E_P24(p21, c8, p24);
+}
+
+#else
+void smbencrypt_dummy(void){}
+#endif
diff --git a/source/locking/locking.c b/source/locking/locking.c
new file mode 100644 (file)
index 0000000..6ff3ab5
--- /dev/null
@@ -0,0 +1,330 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   Locking functions
+   Copyright (C) Andrew Tridgell 1992-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"
+#include "loadparm.h"
+extern int DEBUGLEVEL;
+extern connection_struct Connections[];
+extern files_struct Files[];
+
+pstring share_del_pending="";
+
+
+/****************************************************************************
+  utility function called to see if a file region is locked
+****************************************************************************/
+BOOL is_locked(int fnum,int cnum,uint32 count,uint32 offset)
+{
+  int snum = SNUM(cnum);
+
+  if (count == 0)
+    return(False);
+
+  if (!lp_locking(snum) || !lp_strict_locking(snum))
+    return(False);
+
+  return(fcntl_lock(Files[fnum].fd,F_GETLK,offset,count,
+                   (Files[fnum].can_write?F_WRLCK:F_RDLCK)));
+}
+
+
+/****************************************************************************
+  utility function called by locking requests
+****************************************************************************/
+BOOL do_lock(int fnum,int cnum,uint32 count,uint32 offset,int *eclass,uint32 *ecode)
+{
+  BOOL ok = False;
+
+  if (!lp_locking(SNUM(cnum)))
+    return(True);
+
+  if (count == 0) {
+    *eclass = ERRDOS;
+    *ecode = ERRnoaccess;
+    return False;
+  }
+
+  if (Files[fnum].can_lock && OPEN_FNUM(fnum) && (Files[fnum].cnum == cnum))
+    ok = fcntl_lock(Files[fnum].fd,F_SETLK,offset,count,
+                   (Files[fnum].can_write?F_WRLCK:F_RDLCK));
+
+  if (!ok) {
+    *eclass = ERRDOS;
+    *ecode = ERRlock;
+    return False;
+  }
+  return True; /* Got lock */
+}
+
+
+/****************************************************************************
+  utility function called by unlocking requests
+****************************************************************************/
+BOOL do_unlock(int fnum,int cnum,uint32 count,uint32 offset,int *eclass,uint32 *ecode)
+{
+  BOOL ok = False;
+
+  if (!lp_locking(SNUM(cnum)))
+    return(True);
+
+  if (Files[fnum].can_lock && OPEN_FNUM(fnum) && (Files[fnum].cnum == cnum))
+    ok = fcntl_lock(Files[fnum].fd,F_SETLK,offset,count,F_UNLCK);
+   
+  if (!ok) {
+    *eclass = ERRDOS;
+    *ecode = ERRlock;
+    return False;
+  }
+  return True; /* Did unlock */
+}
+
+/*******************************************************************
+  name a share file
+  ******************************************************************/
+static BOOL share_name(int cnum,struct stat *st,char *name)
+{
+  strcpy(name,lp_lockdir());
+  standard_sub(cnum,name);
+  trim_string(name,"","/");
+  if (!*name) return(False);
+  name += strlen(name);
+  
+  sprintf(name,"/share.%d.%d",(int)st->st_dev,(int)st->st_ino);
+  return(True);
+}
+
+/*******************************************************************
+  use the fnum to get the share file name
+  ******************************************************************/
+static BOOL share_name_fnum(int fnum,char *name)
+{
+  struct stat st;
+  if (fstat(Files[fnum].fd,&st) != 0) return(False);
+  return(share_name(Files[fnum].cnum,&st,name));
+}
+
+
+/*******************************************************************
+  get the share mode of a file using the fnum
+  ******************************************************************/
+int get_share_mode_by_fnum(int cnum,int fnum,int *pid)
+{
+  struct stat sbuf;
+  if (fstat(Files[fnum].fd,&sbuf) == -1) return(0);
+  return(get_share_mode(cnum,&sbuf,pid));
+}
+
+/*******************************************************************
+  get the share mode of a file using the files name
+  ******************************************************************/
+int get_share_mode_byname(int cnum,char *fname,int *pid)
+{
+  struct stat sbuf;
+  if (stat(fname,&sbuf) == -1) return(0);
+  return(get_share_mode(cnum,&sbuf,pid));
+}  
+
+
+/*******************************************************************
+get the share mode of a file
+********************************************************************/
+int get_share_mode(int cnum,struct stat *sbuf,int *pid)
+{
+  pstring fname;
+  int fd2;
+  char buf[16];
+  int ret;
+  time_t t;
+
+  *pid = 0;
+
+  if (!share_name(cnum,sbuf,fname)) return(0);
+
+  fd2 = open(fname,O_RDONLY,0);
+  if (fd2 < 0) return(0);
+
+  if (read(fd2,buf,16) != 16) {
+    close(fd2);
+    unlink(fname);
+    return(0);
+  }
+  close(fd2);
+
+  t = IVAL(buf,0);
+  ret = IVAL(buf,4);
+  *pid = IVAL(buf,8);
+  
+  if (IVAL(buf,12) != LOCKING_VERSION) {    
+    if (!unlink(fname)) DEBUG(2,("Deleted old locking file %s",fname));
+    *pid = 0;
+    return(0);
+  }
+
+  if (*pid && !process_exists(*pid)) {
+    ret=0;
+    *pid = 0;
+  }
+
+  if (! *pid) unlink(fname); /* XXXXX race, race */
+
+  if (*pid)
+    DEBUG(5,("Read share file %s mode 0x%X pid=%d\n",fname,ret,*pid));
+
+  return(ret);
+}
+
+
+/*******************************************************************
+del the share mode of a file, if we set it last
+********************************************************************/
+void del_share_mode(int fnum)
+{
+  pstring fname;
+  int fd2;
+  char buf[16];
+  time_t t=0;
+  int pid=0;
+  BOOL del = False;
+
+  if (!share_name_fnum(fnum,fname)) return;
+
+  fd2 = open(fname,O_RDONLY,0);
+  if (fd2 < 0) return;
+  if (read(fd2,buf,16) != 16)
+    del = True;
+  close(fd2);
+
+  if (!del) {
+    t = IVAL(buf,0);
+    pid = IVAL(buf,8);
+  }
+
+  if (!del)
+    if (IVAL(buf,12) != LOCKING_VERSION || !pid || !process_exists(pid))
+      del = True;
+
+  if (!del && t == Files[fnum].open_time && pid==(int)getpid())
+    del = True;
+
+  if (del) {
+    if (!unlink(fname)) 
+      DEBUG(2,("Deleted share file %s\n",fname));
+    else {
+      DEBUG(3,("Pending delete share file %s\n",fname));
+      if (*share_del_pending) DEBUG(0,("Share del clash!\n"));
+      strcpy(share_del_pending,fname);
+    }
+  }
+}
+  
+
+/*******************************************************************
+set the share mode of a file
+********************************************************************/
+BOOL set_share_mode(int fnum,int mode)
+{
+  pstring fname;
+  int fd2;
+  char buf[16];
+  int pid = (int)getpid();
+
+  if (!share_name_fnum(fnum,fname)) return(False);
+
+  {
+    int old_umask = umask(0);
+    fd2 = open(fname,O_WRONLY|O_CREAT|O_TRUNC,0644);
+    umask(old_umask);
+  }
+  if (fd2 < 0) {
+    DEBUG(2,("Failed to create share file %s\n",fname));
+    return(False);
+  }
+
+  SIVAL(buf,0,Files[fnum].open_time);
+  SIVAL(buf,4,mode);
+  SIVAL(buf,8,pid);
+  SIVAL(buf,12,LOCKING_VERSION);
+
+  if (write(fd2,buf,16) != 16) {
+    close(fd2);
+    unlink(fname);
+    return(False);
+  }
+
+  write(fd2,Files[fnum].name,strlen(Files[fnum].name)+1);
+
+  close(fd2);
+
+  DEBUG(3,("Created share file %s with mode 0x%X pid=%d\n",fname,mode,pid));
+
+  return(True);
+}
+  
+
+/*******************************************************************
+cleanup any stale share files
+********************************************************************/
+void clean_share_files(void)
+{
+  char *lockdir = lp_lockdir();
+  void *dir;
+  char *s;
+
+  if (!*lockdir) return;
+
+  dir = opendir(lockdir);
+  if (!dir) return;
+
+  while ((s=readdirname(dir))) {
+    char buf[16];
+    int pid;
+    int fd;
+    pstring lname;
+    int dev,inode;
+
+    if (sscanf(s,"share.%d.%d",&dev,&inode)!=2) continue;
+
+    strcpy(lname,lp_lockdir());
+    trim_string(lname,NULL,"/");
+    strcat(lname,"/");
+    strcat(lname,s);
+
+    fd = open(lname,O_RDONLY,0);
+    if (fd < 0) continue;
+
+    if (read(fd,buf,16) != 16) {
+      close(fd);
+      if (!unlink(lname))
+       printf("Deleted corrupt share file %s\n",s);
+      continue;
+    }
+    close(fd);
+
+    pid = IVAL(buf,8);
+
+    if (IVAL(buf,12) != LOCKING_VERSION || !process_exists(pid)) {
+      if (!unlink(lname))
+       printf("Deleted stale share file %s\n",s);
+    }
+  }
+
+  closedir(dir);
+}
diff --git a/source/md4.h b/source/md4.h
new file mode 100644 (file)
index 0000000..3f60d75
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+   This code is from rfc1186. 
+*/
+
+ /*
+ ** ********************************************************************
+ ** md4.h -- Header file for implementation of                        **
+ ** MD4 Message Digest Algorithm                                      **
+ ** Updated: 2/13/90 by Ronald L. Rivest                              **
+ ** (C) 1990 RSA Data Security, Inc.                                  **
+ ** ********************************************************************
+ */
+
+ /* MDstruct is the data structure for a message digest computation.
+ */
+ typedef struct {
+   unsigned int buffer[4]; /* Holds 4-word result of MD computation */
+   unsigned char count[8]; /* Number of bits processed so far */
+   unsigned int done;      /* Nonzero means MD computation finished */
+ } MDstruct, *MDptr;
+
+ /* MDbegin(MD)
+
+
+
+ ** Input: MD -- an MDptr
+ ** Initialize the MDstruct prepatory to doing a message digest
+ ** computation.
+ */
+ extern void MDbegin();
+
+ /* MDupdate(MD,X,count)
+ ** Input: MD -- an MDptr
+ **        X -- a pointer to an array of unsigned characters.
+ **        count -- the number of bits of X to use (an unsigned int).
+ ** Updates MD using the first "count" bits of X.
+ ** The array pointed to by X is not modified.
+ ** If count is not a multiple of 8, MDupdate uses high bits of
+ ** last byte.
+ ** This is the basic input routine for a user.
+ ** The routine terminates the MD computation when count < 512, so
+ ** every MD computation should end with one call to MDupdate with a
+ ** count less than 512.  Zero is OK for a count.
+ */
+ extern void MDupdate();
+
+ /* MDprint(MD)
+ ** Input: MD -- an MDptr
+ ** Prints message digest buffer MD as 32 hexadecimal digits.
+ ** Order is from low-order byte of buffer[0] to high-order byte
+ ** of buffer[3].
+ ** Each byte is printed with high-order hexadecimal digit first.
+ */
+ extern void MDprint();
+
+ /*
+ ** End of md4.h
+ */
diff --git a/source/nameserv.c b/source/nameserv.c
new file mode 100644 (file)
index 0000000..802b98e
--- /dev/null
@@ -0,0 +1,2318 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   NBT netbios routines and daemon - version 2
+   Copyright (C) Andrew Tridgell 1994-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"
+#include "loadparm.h"
+#include "nameserv.h"
+
+
+static void queue_packet(struct packet_struct *packet);
+void process(void);
+static void dump_names(void);
+static void announce_request(char *group);
+void sync_browse_lists(char *name,int name_type,char *myname,
+                      char *domain,struct in_addr ip);
+
+extern int DEBUGLEVEL;
+
+extern pstring debugf;
+pstring servicesf = CONFIGFILE;
+
+extern pstring scope;
+
+extern BOOL CanRecurse;
+
+extern struct in_addr myip;
+extern struct in_addr bcast_ip;
+extern struct in_addr Netmask;
+extern pstring myhostname;
+static pstring host_file;
+static pstring myname="";
+
+static int ClientNMB= -1;
+static int ClientDGRAM= -1;
+
+static BOOL needannounce=True;
+
+/* this is our name database */
+static struct name_record *namelist = NULL;
+
+/* list of servers to be returned by NetServerEnum */
+static struct server_record *serverlist = NULL;
+
+/* this is the domain list. For the moment we will assume that our
+   primary domain is the first one listed in this list */
+static struct domain_record *domainlist = NULL;
+
+/* are we running as a daemon ? */
+static BOOL is_daemon = False;
+
+/* machine comment for host announcements */
+static pstring ServerComment="";
+
+static BOOL got_bcast = False;
+static BOOL got_myip = False;
+static BOOL got_nmask = False;
+
+static BOOL updatedlists = False;
+static int  updatecount=0;
+
+/* what server type are we currently */
+static int ServerType = 
+SV_TYPE_WORKSTATION | SV_TYPE_SERVER | SV_TYPE_TIME_SOURCE |
+SV_TYPE_SERVER_UNIX |
+SV_TYPE_PRINTQ_SERVER | SV_TYPE_POTENTIAL_BROWSER;
+
+/* here are my election parameters */
+
+/* NTAS uses 2, NT uses 1, WfWg uses 0 */
+#define MAINTAIN_LIST 1
+#define ELECTION_VERSION 1
+
+static BOOL RunningElection = False;
+static BOOL needelection = False;
+static int ElectionCount = 0;
+static int StartupTime =0;
+
+
+/* WfWg uses 01040b01 */
+/* Win95 uses 01041501 */
+/* NTAS uses ?? */
+static uint32 ElectionCriterion = (MAINTAIN_LIST<<1)|(ELECTION_VERSION<<8);
+
+/* we currently support being the master for just one group. Being the
+   master for more than one group might be tricky as NetServerEnum is
+   often asked for a list without naming the group */
+static fstring PrimaryGroup="";
+
+#define AM_MASTER (PrimaryGroup[0] && (ServerType & SV_TYPE_MASTER_BROWSER))
+
+#define MSBROWSE "\001\002__MSBROWSE__\002"
+
+#define GET_TTL(ttl) ((ttl)?MIN(ttl,lp_max_ttl()):lp_max_ttl())
+
+#define BROWSE_MAILSLOT "\\MAILSLOT\\BROWSE"
+
+/****************************************************************************
+catch a sighup
+****************************************************************************/
+static int sig_hup()
+{
+  BlockSignals(True);
+
+  DEBUG(0,("Got SIGHUP (reload not implemented)\n"));
+  dump_names();
+  reload_services(True);
+
+  BlockSignals(False);
+#ifndef DONT_REINSTALL_SIG
+  signal(SIGHUP,SIGNAL_CAST sig_hup);
+#endif
+  return(0);
+}
+
+/****************************************************************************
+catch a sigpipe
+****************************************************************************/
+static int sig_pipe()
+{
+  BlockSignals(True);
+
+  DEBUG(0,("Got SIGPIPE\n"));
+  if (!is_daemon)
+    exit(1);
+  BlockSignals(False);
+  return(0);
+}
+
+#if DUMP_CORE
+/*******************************************************************
+prepare to dump a core file - carefully!
+********************************************************************/
+static BOOL dump_core(void)
+{
+  char *p;
+  pstring dname;
+  strcpy(dname,debugf);
+  if ((p=strrchr(dname,'/'))) *p=0;
+  strcat(dname,"/corefiles");
+  mkdir(dname,0700);
+  sys_chown(dname,getuid(),getgid());
+  chmod(dname,0700);
+  if (chdir(dname)) return(False);
+  umask(~(0700));
+
+#ifndef NO_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",rlp.rlim_cur,rlp.rlim_max));
+  }
+#endif
+#endif
+
+
+  DEBUG(0,("Dumping core in %s\n",dname));
+  return(True);
+}
+#endif
+
+
+/****************************************************************************
+possibly continue after a fault
+****************************************************************************/
+static void fault_continue(void)
+{
+  static int errcount=1;
+
+  errcount--;
+
+  if (is_daemon && errcount)
+    process();
+
+#if DUMP_CORE
+    if (dump_core()) return;
+#endif
+
+  return;
+}
+
+
+/*******************************************************************
+  wrapper to get the DC
+  ******************************************************************/
+static char *domain_controller(void)
+{
+  char *dc = lp_domain_controller();
+  /* so many people mistake this for a bool that we need to handle it. sigh. */
+  if (!*dc || strequal(dc,"yes") || strequal(dc,"true"))
+    strcpy(dc,myname);
+  return(dc);
+}
+
+
+
+/****************************************************************************
+  true if two netbios names are equal
+****************************************************************************/
+static BOOL name_equal(struct nmb_name *n1,struct nmb_name *n2)
+{
+  if (n1->name_type != n2->name_type) return(False);
+
+  return(strequal(n1->name,n2->name) && strequal(n1->scope,n2->scope));
+}
+
+/****************************************************************************
+  add a netbios name into the namelist
+  **************************************************************************/
+static void add_name(struct name_record *n)
+{
+  struct name_record *n2;
+
+  if (!namelist) {
+    namelist = n;
+    n->prev = NULL;
+    n->next = NULL;
+    return;
+  }
+
+  for (n2 = namelist; n2->next; n2 = n2->next) ;
+
+  n2->next = n;
+  n->next = NULL;
+  n->prev = n2;
+}
+
+/****************************************************************************
+  add a domain into the list
+  **************************************************************************/
+static void add_domain(struct domain_record *d)
+{
+  struct domain_record *d2;
+
+  if (!domainlist) {
+    domainlist = d;
+    d->prev = NULL;
+    d->next = NULL;
+    return;
+  }
+
+  for (d2 = domainlist; d2->next; d2 = d2->next) ;
+
+  d2->next = d;
+  d->next = NULL;
+  d->prev = d2;
+}
+
+
+/****************************************************************************
+  add a server into the list
+  **************************************************************************/
+static void add_server(struct server_record *s)
+{
+  struct server_record *s2;
+
+  if (!serverlist) {
+    serverlist = s;
+    s->prev = NULL;
+    s->next = NULL;
+    return;
+  }
+
+  for (s2 = serverlist; s2->next; s2 = s2->next) ;
+
+  s2->next = s;
+  s->next = NULL;
+  s->prev = s2;
+}
+
+/****************************************************************************
+  remove a name from the namelist. The pointer must be an element just 
+  retrieved
+  **************************************************************************/
+static void remove_name(struct name_record *n)
+{
+  struct name_record *nlist = namelist;
+  while (nlist && nlist != n) nlist = nlist->next;
+  if (nlist) {
+    if (nlist->next) nlist->next->prev = nlist->prev;
+    if (nlist->prev) nlist->prev->next = nlist->next;
+    free(nlist);
+  }
+}
+
+/****************************************************************************
+  find a name in the namelist 
+  **************************************************************************/
+static struct name_record *find_name(struct nmb_name *n)
+{
+  struct name_record *ret;
+  for (ret = namelist; ret; ret = ret->next)
+    if (name_equal(&ret->name,n)) return(ret);
+
+  return(NULL);
+}
+
+/****************************************************************************
+  dump a copy of the name table
+  **************************************************************************/
+static void dump_names(void)
+{
+  time_t t = time(NULL);
+  struct name_record *n;
+  struct domain_record *d;
+
+  DEBUG(3,("Dump of local name table:\n"));
+
+  for (n = namelist; n; n = n->next) {
+    DEBUG(3,("%s %s TTL=%d Unique=%s\n",
+            namestr(&n->name),
+            inet_ntoa(n->ip),
+            n->death_time?n->death_time-t:0,
+            BOOLSTR(n->unique)));
+    }
+
+  DEBUG(3,("\nDump of domain list:\n"));
+  for (d = domainlist; d; d = d->next)
+    DEBUG(3,("%s %s\n",d->name,inet_ntoa(d->bcast_ip)));
+}
+
+
+/****************************************************************************
+  add a host entry to the name list
+  ****************************************************************************/
+static struct name_record *add_host_entry(char *name,int type,BOOL unique,int ttl,
+                                         enum name_source source,
+                                         struct in_addr ip)
+{
+  struct name_record *n;
+  struct name_record *n2=NULL;
+
+  n = (struct name_record *)malloc(sizeof(*n));
+  if (!n) return(NULL);
+
+  bzero((char *)n,sizeof(*n));
+
+  make_nmb_name(&n->name,name,type,scope);
+  if ((n2=find_name(&n->name))) {
+    free(n);
+    n = n2;
+  }
+
+  if (ttl) n->death_time = time(NULL)+ttl*3;
+  n->ip = ip;
+  n->unique = unique;
+  n->source = source;
+  
+  if (!n2) add_name(n);
+
+  DEBUG(3,("Added host entry %s at %s ttl=%d unique=%s\n",
+          namestr(&n->name),inet_ntoa(ip),ttl,BOOLSTR(unique)));
+
+  return(n);
+}
+
+
+/****************************************************************************
+  add a domain entry
+  ****************************************************************************/
+static struct domain_record *add_domain_entry(char *name,struct in_addr ip)
+{
+  struct domain_record *d;
+
+  d = (struct domain_record *)malloc(sizeof(*d));
+
+  if (!d) return(NULL);
+
+  bzero((char *)d,sizeof(*d));
+
+  if (zero_ip(ip)) ip = bcast_ip;
+
+  StrnCpy(d->name,name,sizeof(d->name)-1);
+  d->bcast_ip = ip;
+
+  if (!PrimaryGroup[0] && ip_equal(bcast_ip,ip) && name[0] != '*') {
+    strcpy(PrimaryGroup,name);
+    strupper(PrimaryGroup);
+    DEBUG(3,("Setting primary group to %s (%s)\n",PrimaryGroup,inet_ntoa(ip)));
+  }
+
+  add_domain(d);
+
+  ip = *interpret_addr2("255.255.255.255");
+  if (name[0] != '*') add_host_entry(name,0x1e,False,0,SELF,ip);         
+
+  DEBUG(3,("Added domain entry %s at %s\n",
+          name,inet_ntoa(ip)));
+
+  return(d);
+}
+
+/****************************************************************************
+  add a server entry
+  ****************************************************************************/
+struct server_record *add_server_entry(char *name,int servertype,
+                                      int ttl,char *comment,BOOL replace)
+{
+  BOOL newentry=False;
+  struct server_record *s;
+
+  for (s = serverlist; s; s = s->next)
+    if (strequal(name,s->name)) break;
+
+  if (s && !replace) {
+    DEBUG(4,("Not replacing %s\n",name));
+    return(s);
+  }
+
+  updatedlists=True;
+
+  if (!s) {
+    newentry = True;
+    s = (struct server_record *)malloc(sizeof(*s));
+
+    if (!s) return(NULL);
+
+    bzero((char *)s,sizeof(*s));
+  }
+
+  /* update the entry */
+  StrnCpy(s->name,name,sizeof(s->name)-1);
+  StrnCpy(s->comment,comment,sizeof(s->comment)-1);
+  s->servertype = servertype;
+  s->death_time = ttl?time(NULL)+ttl*3:0;
+  strupper(s->name);
+  if (s->servertype & SV_TYPE_DOMAIN_ENUM) strupper(s->comment);
+
+  if (!newentry) return(s);
+
+  add_server(s);
+
+  if (newentry) {
+    DEBUG(3,("Added server entry %s of type %x (%s)\n",
+            name,servertype,comment));
+  } else {
+    DEBUG(3,("Updated server entry %s of type %x (%s)\n",
+            name,servertype,comment));
+  }
+
+  return(s);
+}
+
+
+/****************************************************************************
+  add the magic samba names, useful for finding samba servers
+  **************************************************************************/
+static void add_my_names(void)
+{
+  struct in_addr ip;
+
+  ip = *interpret_addr2("0.0.0.0");
+
+  add_host_entry(myname,0x20,True,0,SELF,ip);
+  add_host_entry(myname,0x0,True,0,SELF,ip);
+  add_host_entry(myname,0x1f,True,0,SELF,ip); /* used for chat?? */
+  add_host_entry(myname,0x3,True,0,SELF,ip); /* used for winpopup */
+                                               
+  if (!domainlist)
+    add_domain_entry(lp_workgroup(),bcast_ip);
+  add_server_entry(myname,
+                  ServerType,
+                  0,ServerComment,True);
+
+  add_host_entry("__SAMBA__",0x20,True,0,SELF,ip);
+  add_host_entry("__SAMBA__",0x0,True,0,SELF,ip);
+
+  if (lp_preferred_master()) {
+    DEBUG(3,("Preferred master startup\n"));
+    needelection = True;
+    ElectionCriterion |= (1<<3);
+  }
+
+  ElectionCriterion |= (lp_os_level() << 24);
+}
+
+
+/*******************************************************************
+  write out browse.dat
+  ******************************************************************/
+static void write_browse_list(void)
+{
+  struct server_record *s;
+  pstring fname,fnamenew;
+  FILE *f;
+  
+  updatecount++;
+
+  strcpy(fname,lp_lockdir());
+  trim_string(fname,NULL,"/");
+  strcat(fname,"/");
+  strcat(fname,SERVER_LIST);
+  strcpy(fnamenew,fname);
+  strcat(fnamenew,".");
+  
+  f = fopen(fnamenew,"w");
+  
+  if (!f) {
+    DEBUG(4,("Can't open %s - %s\n",fnamenew,strerror(errno)));
+    return;
+  }
+  
+  for (s=serverlist; s ; s = s->next) {
+    /* don't list domains I don't have a master for */
+    if ((s->servertype & SV_TYPE_DOMAIN_ENUM) && !s->comment[0]) continue;
+       
+    fprintf(f,"\"%s\"\t%08x\t\"%s\"\n",s->name,s->servertype,s->comment);
+  }
+  
+  
+  fclose(f);
+  chmod(fnamenew,0644);
+  /* unlink(fname); */
+  rename(fnamenew,fname);   
+  DEBUG(3,("Wrote browse list %s\n",fname));
+}
+
+/*******************************************************************
+  expire old names in the namelist and serverlist
+  ******************************************************************/
+static void expire_names(void)
+{
+  static time_t lastrun=0;
+  time_t t = time(NULL);
+  struct name_record *n;
+  struct name_record *next;
+  struct server_record *s;
+  struct server_record *nexts;
+
+  if (!lastrun) lastrun = t;
+  if (t < lastrun + 5) return;
+  lastrun = t;
+
+  /* expire old names */
+  for (n = namelist; n; n = next) {
+    if (n->death_time && n->death_time < t) {
+      DEBUG(3,("Removing dead name %s\n",
+              namestr(&n->name)));
+      next = n->next;
+      if (n->prev) n->prev->next = n->next;
+      if (n->next) n->next->prev = n->prev;
+      if (namelist == n) namelist = n->next; 
+      free(n);
+    } else {
+      next = n->next;
+    }
+  }
+
+  /* expire old entries in the serverlist */
+  for (s = serverlist; s; s = nexts) {
+    if (s->death_time && s->death_time < t) {
+      DEBUG(3,("Removing dead server %s\n",s->name));
+      updatedlists = True;
+      nexts = s->next;
+      if (s->prev) s->prev->next = s->next;
+      if (s->next) s->next->prev = s->prev;
+      if (serverlist == s) serverlist = s->next; 
+      free(s);
+    } else {
+      nexts = s->next;
+    }
+  }
+}
+
+
+/*******************************************************************
+  delete old names from the namelist
+  ******************************************************************/
+static void housekeeping(void)
+{
+  time_t t = time(NULL);
+
+  expire_names();
+
+  /* write out the browse.dat database for smbd to get */
+  if (updatedlists) {
+    write_browse_list();
+    updatedlists = False;
+  }
+
+  {
+    /* occasionally check to see if the master browser is around */
+    static time_t lastrun=0;
+    if (!lastrun) lastrun = t;
+    if (t < lastrun + 5*60) return;
+    lastrun = t;
+
+    if (!AM_MASTER && PrimaryGroup[0] &&
+       !name_query(ClientNMB,PrimaryGroup,0x1d,True,False,
+                   bcast_ip,NULL,queue_packet)) {
+      DEBUG(2,("Forcing election on %s\n",PrimaryGroup));
+      needelection = True;
+    }
+  }
+}
+
+
+/****************************************************************************
+  reload the services file
+  **************************************************************************/
+BOOL reload_services(BOOL test)
+{
+  BOOL ret;
+  extern fstring remote_machine;
+
+  strcpy(remote_machine,"nmbd");
+
+  if (lp_loaded())
+    {
+      pstring fname;
+      strcpy(fname,lp_configfile());
+      if (file_exist(fname,NULL) && !strcsequal(fname,servicesf))
+       {
+         strcpy(servicesf,fname);
+         test = False;
+       }
+    }
+
+  if (test && !lp_file_list_changed())
+    return(True);
+
+  ret = lp_load(servicesf,True);
+
+  /* perhaps the config filename is now set */
+  if (!test)
+    reload_services(True);
+
+  return(ret);
+}
+
+
+
+/****************************************************************************
+load a netbios hosts file
+****************************************************************************/
+static void load_hosts_file(char *fname)
+{
+  FILE *f = fopen(fname,"r");
+  pstring line;
+  if (!f) {
+    DEBUG(2,("Can't open lmhosts file %s\n",fname));
+    return;
+  }
+
+  while (!feof(f))
+    {
+      if (!fgets_slash(line,sizeof(pstring),f)) continue;
+      
+      if (*line == '#') continue;
+
+      {
+       BOOL group=False;
+       string ip,name,flags,extra;
+       char *ptr;
+       int count = 0;
+       struct in_addr ipaddr;
+       enum name_source source = LMHOSTS;
+
+       *ip = *name = *flags = *extra = 0;
+
+       ptr = line;
+
+       if (next_token(&ptr,ip,NULL)) ++count;
+       if (next_token(&ptr,name,NULL)) ++count;
+       if (next_token(&ptr,flags,NULL)) ++count;
+       if (next_token(&ptr,extra,NULL)) ++count;
+
+       if (count <= 0) continue;
+
+       if (count > 0 && count < 2)
+         {
+           DEBUG(0,("Ill formed hosts line [%s]\n",line));         
+           continue;
+         }
+
+       if (strchr(flags,'G') || strchr(flags,'S'))
+         group = True;
+
+       if (strchr(flags,'M') && !group) {
+         source = SELF;
+         strcpy(myname,name);
+       }
+
+       ipaddr = *interpret_addr2(ip);
+
+       if (group) {
+         add_domain_entry(name,ipaddr);
+       } else {
+         add_host_entry(name,0x20,True,0,source,ipaddr);
+       }
+      }
+    }
+
+  fclose(f);
+}
+
+/*******************************************************************
+  check if 2 IPs are on the same net
+  we will assume the local netmask, although this could be wrong XXXX
+  ******************************************************************/
+static BOOL same_net(struct in_addr ip1,struct in_addr ip2)
+{
+  unsigned long net1,net2,nmask;
+
+  nmask = ntohl(Netmask.s_addr);
+  net1 = ntohl(ip1.s_addr);
+  net2 = ntohl(ip2.s_addr);
+           
+  return((net1 & nmask) == (net2 & nmask));
+}
+
+/****************************************************************************
+  send an election packet
+  **************************************************************************/
+static void send_election(char *group,uint32 criterion,int timeup,char *name)
+{
+  pstring outbuf;
+  char *p;
+
+  DEBUG(2,("Sending election to %s for workgroup %s\n",
+          inet_ntoa(bcast_ip),group));    
+
+  bzero(outbuf,sizeof(outbuf));
+  p = outbuf;
+  CVAL(p,0) = 8; /* election */
+  p++;
+
+  CVAL(p,0) = ELECTION_VERSION;
+  SIVAL(p,1,criterion);
+  SIVAL(p,5,timeup*1000); /* ms - despite the spec */
+  p += 13;
+  strcpy(p,name);
+  strupper(p);
+  p = skip_string(p,1);
+
+  send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
+                     name,group,0,0x1e,bcast_ip,myip);
+}
+
+
+/****************************************************************************
+  send a backup list response
+  **************************************************************************/
+static void send_backup_list(char *name,int token,struct nmb_name *to,
+                            struct in_addr ip)
+{
+  pstring outbuf;
+  char *p;
+
+  DEBUG(2,("Sending backup list to %s for workgroup %s\n",
+          inet_ntoa(ip),PrimaryGroup));           
+
+  bzero(outbuf,sizeof(outbuf));
+  p = outbuf;
+  CVAL(p,0) = 10; /* backup list response */
+  p++;
+
+  CVAL(p,0) = 1; /* count */
+  SIVAL(p,1,token);
+  p += 5; 
+  strcpy(p,name);
+  strupper(p);
+  p = skip_string(p,1) + 1;
+
+  send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
+                     myname,to->name,0,to->name_type,ip,myip);
+}
+
+
+/*******************************************************************
+  become the master browser
+  ******************************************************************/
+static void become_master(void)
+{
+  uint32 domain_type = SV_TYPE_DOMAIN_ENUM | SV_TYPE_SERVER_UNIX;
+  DEBUG(2,("Becoming master for %s\n",PrimaryGroup));
+
+  ServerType |= SV_TYPE_MASTER_BROWSER;
+  ServerType |= SV_TYPE_BACKUP_BROWSER;
+  ElectionCriterion |= 0x5;
+
+  add_host_entry(PrimaryGroup,0x1d,True,0,SELF,myip);
+  add_host_entry(PrimaryGroup,0x0,False,0,SELF,myip);
+  add_host_entry(MSBROWSE,1,False,0,SELF,myip);
+
+  if (lp_domain_master()) {
+    add_host_entry(myname,0x1b,True,0,SELF,myip);
+    add_host_entry(PrimaryGroup,0x1b,True,0,SELF,myip);
+    add_host_entry(PrimaryGroup,0x1c,False,0,SELF,myip);
+    ServerType |= SV_TYPE_DOMAIN_MASTER;
+    if (lp_domain_logons()) {
+      ServerType |= SV_TYPE_DOMAIN_CTRL;
+      ServerType |= SV_TYPE_DOMAIN_MEMBER;
+      domain_type |= SV_TYPE_DOMAIN_CTRL;
+    }
+  }
+
+  add_server_entry(PrimaryGroup,domain_type,0,myname,True);
+  add_server_entry(myname,ServerType,0,ServerComment,True);
+
+  announce_request(PrimaryGroup);
+
+  needannounce = True;
+}
+
+
+/*******************************************************************
+  unbecome the master browser
+  ******************************************************************/
+static void become_nonmaster(void)
+{
+  struct name_record *n;
+  struct nmb_name nn;
+
+  DEBUG(2,("Becoming non-master for %s\n",PrimaryGroup));
+
+  ServerType &= ~SV_TYPE_MASTER_BROWSER;
+  ServerType &= ~SV_TYPE_DOMAIN_CTRL;
+  ServerType &= ~SV_TYPE_DOMAIN_MASTER;
+
+  ElectionCriterion &= ~0x4;
+
+  make_nmb_name(&nn,PrimaryGroup,0x1d,scope);
+  n = find_name(&nn);
+  if (n && n->source == SELF) remove_name(n);
+
+  make_nmb_name(&nn,PrimaryGroup,0x1b,scope);
+  n = find_name(&nn);
+  if (n && n->source == SELF) remove_name(n);
+
+  make_nmb_name(&nn,MSBROWSE,1,scope);
+  n = find_name(&nn);
+  if (n && n->source == SELF) remove_name(n);
+}
+
+
+/*******************************************************************
+  run the election
+  ******************************************************************/
+static void run_election(void)
+{
+  time_t t = time(NULL);
+  static time_t lastime = 0;
+
+  if (!PrimaryGroup[0] || !RunningElection) return;
+
+  /* send election packets once a second */
+  if (lastime &&
+      t-lastime <= 0) return;
+
+  lastime = t;
+
+  send_election(PrimaryGroup,ElectionCriterion,t-StartupTime,myname);
+
+  if (ElectionCount++ < 4) return;
+   
+  /* I won! now what :-) */
+  RunningElection = False;
+  DEBUG(2,(">>> Won election on %s <<<\n",PrimaryGroup));
+  become_master();
+}
+
+
+/****************************************************************************
+  construct a host announcement unicast
+  **************************************************************************/
+static void announce_host(struct domain_record *d,char *my_name,char *comment)
+{
+  time_t t = time(NULL);
+  pstring outbuf;
+  char *p;
+  char *namep;
+  char *stypep;
+  char *commentp;
+  uint32 stype = ServerType;
+
+  if (needannounce) {
+    /* drop back to a max 3 minute announce - this is to prevent a
+       single lost packet from stuffing things up for too long */
+    d->announce_interval = MIN(d->announce_interval,3*60);
+    d->lastannounce_time = t - (d->announce_interval+1);
+  }
+
+  /* announce every minute at first then progress to every 12 mins */
+  if (d->lastannounce_time && 
+      (t - d->lastannounce_time) < d->announce_interval)
+    return;
+
+  if (d->announce_interval < 12*60) d->announce_interval += 60;
+  d->lastannounce_time = t;
+
+  DEBUG(2,("Sending announcement to %s for workgroup %s\n",
+          inet_ntoa(d->bcast_ip),d->name));
+
+  if (!strequal(PrimaryGroup,d->name) ||
+      !ip_equal(bcast_ip,d->bcast_ip)) {
+    stype &= ~(SV_TYPE_POTENTIAL_BROWSER | SV_TYPE_MASTER_BROWSER |
+              SV_TYPE_DOMAIN_MASTER | SV_TYPE_BACKUP_BROWSER |
+              SV_TYPE_DOMAIN_CTRL | SV_TYPE_DOMAIN_MEMBER);
+  }
+
+  if (!*comment) comment = "NoComment";
+  if (!*my_name) my_name = "NoName";
+
+  if (strlen(comment) > 43) comment[43] = 0;  
+
+  bzero(outbuf,sizeof(outbuf));
+  CVAL(outbuf,0) = 1; /* host announce */
+  p = outbuf+1;
+
+  CVAL(p,0) = updatecount;
+  SIVAL(p,1,d->announce_interval*1000); /* ms - despite the spec */
+  namep = p+5;
+  StrnCpy(p+5,my_name,16);
+  strupper(p+5);
+  CVAL(p,21) = 2; /* major version */
+  CVAL(p,22) = 2; /* minor version */
+  stypep = p+23;
+  SIVAL(p,23,stype);
+  SSVAL(p,27,0xaa55); /* browse signature */
+  SSVAL(p,29,1); /* browse version */
+  commentp = p+31;
+  strcpy(p+31,comment);
+  p += 31;
+  p = skip_string(p,1);
+
+  send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
+                     my_name,d->name,0,0x1d,d->bcast_ip,myip);
+
+  /* if I'm the master then I also need to do a local master and
+     domain announcement */
+
+  if (AM_MASTER &&
+      strequal(d->name,PrimaryGroup) &&
+      ip_equal(bcast_ip,d->bcast_ip)) {
+
+    /* do master announcements as well */
+    SIVAL(stypep,0,ServerType);
+
+    CVAL(outbuf,0) = 15; /* local master announce */
+    send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
+                       my_name,PrimaryGroup,0,0x1e,d->bcast_ip,myip);
+
+    CVAL(outbuf,0) = 12; /* domain announce */
+    StrnCpy(namep,PrimaryGroup,15);
+    strupper(namep);
+    StrnCpy(commentp,myname,15);
+    strupper(commentp);
+    SIVAL(stypep,0,(unsigned)0x80000000);
+    p = commentp + strlen(commentp) + 1;
+
+    send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
+                       my_name,MSBROWSE,0,1,d->bcast_ip,myip);
+  }
+}
+
+
+/****************************************************************************
+  send a announce request to the local net
+  **************************************************************************/
+static void announce_request(char *group)
+{
+  pstring outbuf;
+  char *p;
+
+  DEBUG(2,("Sending announce request to %s for workgroup %s\n",
+          inet_ntoa(bcast_ip),group));
+
+  bzero(outbuf,sizeof(outbuf));
+  p = outbuf;
+  CVAL(p,0) = 2; /* announce request */
+  p++;
+
+  CVAL(p,0) = 0; /* flags?? */
+  p++;
+  StrnCpy(p,myname,16);
+  strupper(p);
+  p = skip_string(p,1);
+
+  send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
+                     myname,group,0,0,bcast_ip,myip);
+}
+
+/****************************************************************************
+  announce myself as a master to the PDC
+  **************************************************************************/
+static void announce_master(char *group)
+{
+  static time_t last=0;
+  time_t t = time(NULL);
+  pstring outbuf;
+  char *p;
+  struct in_addr ip,pdc_ip;
+  fstring pdcname;
+  *pdcname = 0;
+
+  if (strequal(domain_controller(),myname)) return;
+
+  if (!AM_MASTER || (last && (t-last < 10*60))) return;
+  last = t;
+
+  ip = *interpret_addr2(domain_controller());
+
+  if (zero_ip(ip)) ip = bcast_ip;
+
+  if (!name_query(ClientNMB,PrimaryGroup,
+                 0x1b,False,False,ip,&pdc_ip,queue_packet)) {
+    DEBUG(2,("Failed to find PDC at %s\n",domain_controller()));
+    return;
+  }
+
+  name_status(ClientNMB,PrimaryGroup,0x1b,False,
+             pdc_ip,NULL,pdcname,queue_packet);
+
+  if (!pdcname[0]) {
+    DEBUG(3,("Can't find netbios name of PDC at %s\n",inet_ntoa(pdc_ip)));
+  } else {
+    sync_browse_lists(pdcname,0x20,myname,PrimaryGroup,pdc_ip);
+  }
+
+
+  DEBUG(2,("Sending master announce to %s for workgroup %s\n",
+          inet_ntoa(pdc_ip),group));
+
+  bzero(outbuf,sizeof(outbuf));
+  p = outbuf;
+  CVAL(p,0) = 13; /* announce request */
+  p++;
+
+  StrnCpy(p,myname,16);
+  strupper(p);
+  p = skip_string(p,1);
+
+  send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
+                     myname,PrimaryGroup,0x1b,0,pdc_ip,myip);
+}
+
+
+/*******************************************************************
+  am I listening on a name. Should check name_type as well 
+
+  This is primarily used to prevent us gathering server lists from
+  other workgroups we aren't a part of
+  ******************************************************************/
+static BOOL listening(struct nmb_name *n)
+{
+  if (!strequal(n->scope,scope)) return(False);
+
+  if (strequal(n->name,myname) ||
+      strequal(n->name,PrimaryGroup) ||
+      strequal(n->name,MSBROWSE))
+    return(True);
+
+  return(False);
+}
+
+
+/*******************************************************************
+  process a domain announcement frame
+
+  Announce frames come in 3 types. Servers send host announcements
+  (command=1) to let the master browswer know they are
+  available. Master browsers send local master announcements
+  (command=15) to let other masters and backups that they are the
+  master. They also send domain announcements (command=12) to register
+  the domain
+
+  The comment field of domain announcements contains the master
+  browser name. The servertype is used by NetServerEnum to select
+  resources. We just have to pass it to smbd (via browser.dat) and let
+  the client choose using bit masks.
+  ******************************************************************/
+static void process_announce(struct packet_struct *p,int command,char *buf)
+{
+  struct dgram_packet *dgram = &p->packet.dgram;
+  int update_count = CVAL(buf,0);
+  int ttl = IVAL(buf,1)/1000;
+  char *name = buf+5;
+  int osmajor=CVAL(buf,21);
+  int osminor=CVAL(buf,22);
+  uint32 servertype = IVAL(buf,23);
+  char *comment = buf+31;
+
+  name[15] = 0;  
+  comment[43] = 0;
+  
+  DEBUG(3,("Announce(%d) %s count=%d ttl=%d OS=(%d,%d) type=%08x comment=%s\n",
+          command,name,update_count,ttl,osmajor,osminor,
+          servertype,comment));
+
+  if (strequal(dgram->source_name.name,myname)) return;
+
+  if (!listening(&dgram->dest_name)) return;
+
+  ttl = GET_TTL(ttl);
+
+  /* add them to our browse list */
+  add_server_entry(name,servertype,ttl,comment,True);
+
+}
+
+/*******************************************************************
+  process a master announcement frame
+  ******************************************************************/
+static void process_master_announce(struct packet_struct *p,char *buf)
+{
+  struct dgram_packet *dgram = &p->packet.dgram;
+  char *name = buf;
+
+  name[15] = 0;
+  
+  DEBUG(3,("Master Announce from %s (%s)\n",name,inet_ntoa(p->ip)));
+
+  if (strequal(dgram->source_name.name,myname)) return;
+
+  if (!AM_MASTER || !listening(&dgram->dest_name)) return;
+
+  /* merge browse lists with them */
+  if (lp_domain_master())
+    sync_browse_lists(name,0x20,myname,PrimaryGroup,p->ip);
+}
+
+
+/*******************************************************************
+  process a backup list request
+
+  A client send 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.
+
+  Currently samba only sends back one name in the backup list, its
+  wn. For larger nets we'll have to add backups and send "become
+  backup" requests occasionally.
+  ******************************************************************/
+static void process_backup_list(struct packet_struct *p,char *buf)
+{
+  struct dgram_packet *dgram = &p->packet.dgram;
+  int count = CVAL(buf,0);
+  int token = IVAL(buf,1);
+  
+  DEBUG(3,("Backup request to %s token=%d\n",
+          namestr(&dgram->dest_name),
+          token));
+
+  if (strequal(dgram->source_name.name,myname)) return;
+
+  if (count <= 0) return;
+
+  if (!AM_MASTER || 
+      !strequal(PrimaryGroup,dgram->dest_name.name))
+    return;
+
+  if (!listening(&dgram->dest_name)) return;
+
+  send_backup_list(myname,token,
+                  &dgram->source_name,
+                  p->ip);
+}
+
+
+/*******************************************************************
+  work out if I win an election
+  ******************************************************************/
+static BOOL win_election(int version,uint32 criterion,int timeup,char *name)
+{  
+  time_t t = time(NULL);
+  uint32 mycriterion;
+  if (version > ELECTION_VERSION) return(False);
+  if (version < ELECTION_VERSION) return(True);
+  
+  mycriterion = ElectionCriterion;
+
+  if (criterion > mycriterion) return(False);
+  if (criterion < mycriterion) return(True);
+
+  if (timeup > (t - StartupTime)) return(False);
+  if (timeup < (t - StartupTime)) return(True);
+
+  if (strcasecmp(myname,name) > 0) return(False);
+  
+  return(True);
+}
+
+
+/*******************************************************************
+  process a election packet
+
+  An election dynamically decides who will be the master. 
+  ******************************************************************/
+static void process_election(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 *name = buf+13;
+
+  name[15] = 0;  
+  
+  DEBUG(3,("Election request from %s vers=%d criterion=%08x timeup=%d\n",
+          name,version,criterion,timeup));
+
+  if (strequal(dgram->source_name.name,myname)) return;
+
+  if (!listening(&dgram->dest_name)) return;
+
+  if (win_election(version,criterion,timeup,name)) {
+    if (!RunningElection) {
+      needelection = True;
+      ElectionCount=0;
+    }
+  } else {
+    needelection = False;
+    if (RunningElection) {
+      RunningElection = False;
+      DEBUG(3,(">>> Lost election on %s <<<\n",PrimaryGroup));
+
+      /* if we are the master then remove our masterly names */
+      if (AM_MASTER)
+       become_nonmaster();
+    }
+  }
+}
+
+
+/*******************************************************************
+  process a announcement request
+
+  clients send these when they want everyone to send an announcement
+  immediately. This can cause quite a storm of packets!
+  ******************************************************************/
+static void process_announce_request(struct packet_struct *p,char *buf)
+{
+  struct dgram_packet *dgram = &p->packet.dgram;
+  int flags = CVAL(buf,0);
+  char *name = buf+1;
+
+  name[15] = 0;
+
+  DEBUG(3,("Announce request from %s flags=0x%X\n",name,flags));
+
+  if (strequal(dgram->source_name.name,myname)) return;
+
+  needannounce = True;
+}
+
+
+/****************************************************************************
+process a browse frame
+****************************************************************************/
+static void process_browse_packet(struct packet_struct *p,char *buf,int len)
+{
+  int command = CVAL(buf,0);
+  switch (command) 
+    {
+    case 1: /* host announce */
+    case 12: /* domain announce */
+    case 15: /* local master announce */
+      process_announce(p,command,buf+1);
+      break;
+
+    case 2: /* announce request */
+      process_announce_request(p,buf+1);
+      break;
+
+    case 8: /* election */
+      process_election(p,buf+1);
+      break;
+
+    case 9: /* get backup list */
+      process_backup_list(p,buf+1);
+      break;
+
+    case 13: /* master announcement */
+      process_master_announce(p,buf+1);
+      break;
+    }
+}
+
+
+/****************************************************************************
+  process a domain logon packet
+  **************************************************************************/
+static void process_logon_packet(struct packet_struct *p,char *buf,int len)
+{
+  char *logname,*q;
+  pstring outbuf;
+  struct dgram_packet *dgram = &p->packet.dgram;
+  int code;
+
+  if (!lp_domain_logons()) {
+    DEBUG(3,("No domain logons\n"));
+    return;
+  }
+  if (!listening(&dgram->dest_name)) {
+    DEBUG(4,("Not listening to that domain\n"));
+    return;
+  }
+
+  q = outbuf;
+  bzero(outbuf,sizeof(outbuf));
+
+  code = SVAL(buf,0);
+  switch (code) {
+  case 0:    
+    {
+      char *machine = buf+2;
+      char *user = skip_string(machine,1);
+      logname = skip_string(user,1);
+
+      SSVAL(q,0,6);
+      q += 2;
+      strcpy(q,"\\\\");
+      q += 2;
+      StrnCpy(q,myname,16);
+      strupper(q);
+      q = skip_string(q,1);
+      SSVAL(q,0,0xFFFF);
+      q += 2;
+
+      DEBUG(3,("Domain login request from %s(%s) user=%s\n",
+              machine,inet_ntoa(p->ip),user));
+    }
+    break;
+  case 7:    
+    {
+      char *machine = buf+2;
+      logname = skip_string(machine,1);
+
+      SSVAL(q,0,0xc);
+      q += 2;
+      StrnCpy(q,domain_controller(),16);
+      strupper(q);
+      q = skip_string(q,1);
+      q += PutUniCode(q,domain_controller());
+      q += PutUniCode(q,dgram->dest_name.name);
+      SSVAL(q,0,0xFFFF);
+      q += 2;
+
+      DEBUG(3,("GETDC request from %s(%s)\n",
+              machine,inet_ntoa(p->ip)));
+    }
+    break;
+  default:
+    DEBUG(3,("Unknown domain request %d\n",code));
+    return;
+  }
+
+
+  send_mailslot_reply(logname,ClientDGRAM,outbuf,PTR_DIFF(q,outbuf),
+                     myname,&dgram->source_name.name[0],0,0,p->ip,myip);  
+}
+
+/****************************************************************************
+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 (dgram->header.msg_type != 0x10 &&
+      dgram->header.msg_type != 0x11 &&
+      dgram->header.msg_type != 0x12) {
+    /* don't process error packets etc yet */
+    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);
+
+  DEBUG(3,("datagram from %s to %s for %s of type %d len=%d\n",
+          namestr(&dgram->source_name),namestr(&dgram->dest_name),
+          smb_buf(buf),CVAL(buf2,0),len));
+
+  if (len <= 0) return;
+
+  if (strequal(smb_buf(buf),"\\MAILSLOT\\BROWSE")) {
+    process_browse_packet(p,buf2,len);
+  } else if (strequal(smb_buf(buf),"\\MAILSLOT\\NET\\NETLOGON")) {
+    process_logon_packet(p,buf2,len);
+  }
+
+}
+
+/*******************************************************************
+  find a workgroup using the specified broadcast
+  ******************************************************************/
+static BOOL find_workgroup(char *name,struct in_addr ip)
+{
+  fstring name1;
+  BOOL ret;
+  struct in_addr ipout;
+
+  strcpy(name1,MSBROWSE);
+
+  ret = name_query(ClientNMB,name1,0x1,True,False,ip,&ipout,queue_packet);
+  if (!ret) return(False);
+
+  name_status(ClientNMB,name1,0x1,False,ipout,name,NULL,queue_packet);
+
+  if (name[0] != '*') {
+    DEBUG(2,("Found workgroup %s on broadcast %s\n",name,inet_ntoa(ip)));
+  } else {
+    DEBUG(3,("Failed to find workgroup %s on broadcast %s\n",name,inet_ntoa(ip)));
+  }
+  return(name[0] != '*');
+}
+
+
+/****************************************************************************
+  a hook for announce handling - called every minute
+  **************************************************************************/
+static void do_announcements(void)
+{
+  struct domain_record *d;
+
+  for (d = domainlist; d; d = d->next) {
+    /* if the ip address is 0 then set to the broadcast */
+    if (zero_ip(d->bcast_ip)) d->bcast_ip = bcast_ip;
+
+    /* if the workgroup is '*' then find a workgroup to be part of */
+    if (d->name[0] == '*') {
+      if (!find_workgroup(d->name,d->bcast_ip)) continue;
+      add_host_entry(d->name,0x1e,False,0,SELF,
+                    *interpret_addr2("255.255.255.255"));
+      if (!PrimaryGroup[0] && ip_equal(bcast_ip,d->bcast_ip)) {
+       strcpy(PrimaryGroup,d->name);
+       strupper(PrimaryGroup);
+      }
+    }
+
+    announce_host(d,myname,ServerComment);
+  }
+
+  /* if I have a domain controller then announce to it */
+  if (AM_MASTER)
+    announce_master(PrimaryGroup);
+
+  needannounce=False;
+}
+
+/*******************************************************************
+  check if someone still owns a name
+  ******************************************************************/
+static BOOL confirm_name(struct name_record *n)
+{
+  struct in_addr ipout;
+  BOOL ret = name_query(ClientNMB,n->name.name,
+                       n->name.name_type,False,
+                       False,n->ip,&ipout,queue_packet);
+  return(ret && ip_equal(ipout,n->ip));
+}
+
+/****************************************************************************
+reply to a name release
+****************************************************************************/
+static void reply_name_release(struct packet_struct *p)
+{
+  struct nmb_packet *nmb = &p->packet.nmb;
+  struct packet_struct p2;
+  struct nmb_packet *nmb2;
+  struct res_rec answer_rec;
+  struct in_addr ip;
+  int rcode=0;
+  int nb_flags = nmb->additional->rdata[0];
+  BOOL bcast = nmb->header.nm_flags.bcast;
+  
+
+  putip((char *)&ip,&nmb->additional->rdata[2]);  
+
+  {
+    struct name_record *n = find_name(&nmb->question.question_name);
+    if (n && n->unique && n->source == REGISTER &&
+       ip_equal(ip,n->ip)) {
+      remove_name(n); n = NULL;
+    }
+
+    /* XXXX under what conditions should we reject the removal?? */
+  }
+
+  DEBUG(3,("Name release on name %s rcode=%d\n",
+          namestr(&nmb->question.question_name),rcode));
+
+  if (bcast) return;
+
+  /* Send a NAME RELEASE RESPONSE */
+  p2 = *p;
+  nmb2 = &p2.packet.nmb;
+
+  nmb2->header.response = True;
+  nmb2->header.nm_flags.bcast = False;
+  nmb2->header.nm_flags.recursion_available = CanRecurse;
+  nmb2->header.nm_flags.trunc = False;
+  nmb2->header.nm_flags.authoritative = True; 
+  nmb2->header.qdcount = 0;
+  nmb2->header.ancount = 1;
+  nmb2->header.nscount = 0;
+  nmb2->header.arcount = 0;
+  nmb2->header.rcode = rcode;
+
+  nmb2->answers = &answer_rec;
+  bzero((char *)nmb2->answers,sizeof(*nmb2->answers));
+  
+  nmb2->answers->rr_name = nmb->question.question_name;
+  nmb2->answers->rr_type = nmb->question.question_type;
+  nmb2->answers->rr_class = nmb->question.question_class;
+  nmb2->answers->ttl = 0; 
+  nmb2->answers->rdlength = 6;
+  nmb2->answers->rdata[0] = nb_flags;
+  putip(&nmb2->answers->rdata[2],(char *)&ip);
+
+  send_packet(&p2);
+}
+
+/****************************************************************************
+  reply to a reg request
+  **************************************************************************/
+static void reply_name_reg(struct packet_struct *p)
+{
+  struct nmb_packet *nmb = &p->packet.nmb;
+  char *qname = nmb->question.question_name.name;
+  BOOL wildcard = (qname[0] == '*'); 
+  BOOL bcast = nmb->header.nm_flags.bcast;
+  int ttl = GET_TTL(nmb->additional->ttl);
+  int name_type = nmb->question.question_name.name_type;
+  int nb_flags = nmb->additional->rdata[0];
+  struct packet_struct p2;
+  struct nmb_packet *nmb2;
+  struct res_rec answer_rec;
+  struct in_addr ip;
+  BOOL group = (nb_flags&0x80)?True:False;
+  int rcode = 0;  
+
+  if (wildcard) return;
+
+  putip((char *)&ip,&nmb->additional->rdata[2]);
+
+  if (group) {
+    /* apparently we should return 255.255.255.255 for group queries (email from MS) */
+    ip = *interpret_addr2("255.255.255.255");
+  }
+
+  {
+    struct name_record *n = find_name(&nmb->question.question_name);
+
+    if (n) {
+      if (!group && !ip_equal(ip,n->ip)) {
+       /* check if the previous owner still wants it, 
+          if so reject the registration, otherwise change the owner 
+          and refresh */
+       if (n->source != REGISTER || confirm_name(n)) {
+         rcode = 6;
+       } else {
+         n->ip = ip;
+         n->death_time = ttl?p->timestamp+ttl*3:0;
+         DEBUG(3,("%s changed owner to %s\n",
+                  namestr(&n->name),inet_ntoa(n->ip)));
+       }
+      } else {
+       /* refresh the name */
+       if (n->source != SELF)
+         n->death_time = ttl?p->timestamp + ttl*3:0;
+      }
+    } else {
+      /* add the name to our database */
+      n = add_host_entry(qname,name_type,!group,ttl,REGISTER,ip);
+    }
+  }
+
+  if (bcast) return;
+
+  DEBUG(3,("Name registration for name %s at %s rcode=%d\n",
+          namestr(&nmb->question.question_name),
+          inet_ntoa(ip),rcode));
+
+  /* Send a NAME REGISTRATION RESPONSE */
+  /* a lot of fields get copied from the query. This gives us the IP
+     and port the reply will be sent to etc */
+  p2 = *p;
+  nmb2 = &p2.packet.nmb;
+
+  nmb2->header.opcode = 5; 
+  nmb2->header.response = True;
+  nmb2->header.nm_flags.bcast = False;
+  nmb2->header.nm_flags.recursion_available = CanRecurse;
+  nmb2->header.nm_flags.trunc = False;
+  nmb2->header.nm_flags.authoritative = True; 
+  nmb2->header.qdcount = 0;
+  nmb2->header.ancount = 1;
+  nmb2->header.nscount = 0;
+  nmb2->header.arcount = 0;
+  nmb2->header.rcode = rcode;
+
+  nmb2->answers = &answer_rec;
+  bzero((char *)nmb2->answers,sizeof(*nmb2->answers));
+  
+  nmb2->answers->rr_name = nmb->question.question_name;
+  nmb2->answers->rr_type = nmb->question.question_type;
+  nmb2->answers->rr_class = nmb->question.question_class;
+
+  nmb2->answers->ttl = ttl; 
+  nmb2->answers->rdlength = 6;
+  nmb2->answers->rdata[0] = nb_flags;
+  putip(&nmb2->answers->rdata[2],(char *)&ip);
+
+  send_packet(&p2);  
+}
+
+
+/****************************************************************************
+reply to a name status query
+****************************************************************************/
+static void reply_name_status(struct packet_struct *p)
+{
+  struct nmb_packet *nmb = &p->packet.nmb;
+  char *qname = nmb->question.question_name.name;
+  BOOL wildcard = (qname[0] == '*'); 
+  struct packet_struct p2;
+  struct nmb_packet *nmb2;
+  struct res_rec answer_rec;
+  char *buf;
+  int count;
+  int rcode = 0;
+  struct name_record *n = find_name(&nmb->question.question_name);
+
+  DEBUG(3,("Name status for name %s\n",
+          namestr(&nmb->question.question_name)));
+
+  if (!wildcard && (!n || n->source != SELF)) 
+    return;
+
+  /* Send a POSITIVE NAME STATUS RESPONSE */
+  /* a lot of fields get copied from the query. This gives us the IP
+     and port the reply will be sent to etc */
+  p2 = *p;
+  nmb2 = &p2.packet.nmb;
+
+  nmb2->header.response = True;
+  nmb2->header.nm_flags.bcast = False;
+  nmb2->header.nm_flags.recursion_available = CanRecurse;
+  nmb2->header.nm_flags.trunc = False;
+  nmb2->header.nm_flags.authoritative = True; /* WfWg ignores 
+                                                non-authoritative answers */
+  nmb2->header.qdcount = 0;
+  nmb2->header.ancount = 1;
+  nmb2->header.nscount = 0;
+  nmb2->header.arcount = 0;
+  nmb2->header.rcode = rcode;
+
+  nmb2->answers = &answer_rec;
+  bzero((char *)nmb2->answers,sizeof(*nmb2->answers));
+  
+
+  nmb2->answers->rr_name = nmb->question.question_name;
+  nmb2->answers->rr_type = nmb->question.question_type;
+  nmb2->answers->rr_class = nmb->question.question_class;
+  nmb2->answers->ttl = 0; 
+
+  for (count=0, n = namelist ; n; n = n->next) {
+    if (n->source != SELF) continue;
+    count++;
+  }
+
+  count = MIN(count,400/18); /* XXXX hack, we should calculate exactly
+                               how many will fit */
+
+  
+  buf = &nmb2->answers->rdata[0];
+  SCVAL(buf,0,count);
+  buf += 1;
+
+  for (n = namelist ; n; n = n->next) 
+    {
+      if (n->source != SELF) continue;
+
+      bzero(buf,18);
+      strcpy(buf,n->name.name);
+      strupper(buf);
+      buf[15] = n->name.name_type;
+      buf += 16;
+      buf[0] = 0x4; /* active */
+      if (!n->unique) buf[0] |= 0x80; /* group */
+      buf += 2;
+      count--;
+    }
+
+  /* XXXXXXX we should fill in more fields of the statistics structure */
+  bzero(buf,64);
+  {
+    extern int num_good_sends,num_good_receives;
+    SIVAL(buf,20,num_good_sends);
+    SIVAL(buf,24,num_good_receives);
+  }
+  SIVAL(buf,46,0xFFB8E5); /* undocumented - used by NT */
+
+  buf += 64;
+
+  nmb2->answers->rdlength = PTR_DIFF(buf,&nmb2->answers->rdata[0]);
+
+  send_packet(&p2);
+}
+
+
+
+/****************************************************************************
+reply to a name query
+****************************************************************************/
+static void reply_name_query(struct packet_struct *p)
+{
+  struct nmb_packet *nmb = &p->packet.nmb;
+  char *qname = nmb->question.question_name.name;
+  BOOL wildcard = (qname[0] == '*'); 
+  BOOL bcast = nmb->header.nm_flags.bcast;
+  struct in_addr retip;
+  int name_type = nmb->question.question_name.name_type;
+  struct packet_struct p2;
+  struct nmb_packet *nmb2;
+  struct res_rec answer_rec;
+  int ttl=0;
+  int rcode=0;
+  BOOL unique = True;
+
+  DEBUG(3,("Name query for %s from %s (bcast=%s) - ",
+          namestr(&nmb->question.question_name),
+          inet_ntoa(p->ip),
+          BOOLSTR(bcast)));
+
+  if (wildcard)
+    retip = myip;
+
+  if (!wildcard) {
+    struct name_record *n = find_name(&nmb->question.question_name);
+
+    if (!n) {
+      struct in_addr ip;
+      unsigned long a;
+
+      /* only do DNS lookups if the query is for type 0x20 or type 0x0 */
+      if (name_type != 0x20 && name_type != 0) {
+       DEBUG(3,("not found\n"));
+       return;
+      }
+
+      /* look it up with DNS */      
+      a = interpret_addr(qname);
+
+      putip((char *)&ip,(char *)&a);
+
+      if (!a) {
+       /* no luck with DNS. We could possibly recurse here XXXX */
+       /* if this isn't a bcast then we should send a negative reply XXXX */
+       DEBUG(3,("no recursion\n"));
+       add_host_entry(qname,name_type,True,60*60,DNSFAIL,ip);
+       return;
+      }
+
+      /* add it to our cache of names. give it 2 hours in the cache */
+      n = add_host_entry(qname,name_type,True,2*60*60,DNS,ip);
+
+      /* failed to add it? yikes! */
+      if (!n) return;
+    }
+
+    /* don't respond to bcast queries for group names unless we own them */
+    if (bcast && !n->unique && !n->source == SELF) {
+      DEBUG(3,("no bcast replies\n"));
+      return;
+    }
+
+    /* don't respond to bcast queries for addresses on the same net as the 
+       machine doing the querying unless its our IP */
+    if (bcast && 
+       n->source != SELF && 
+       same_net(n->ip,p->ip)) {
+      DEBUG(3,("same net\n"));      
+      return;
+    }
+
+    /* is our entry already dead? */
+    if (n->death_time) {
+      if (n->death_time < p->timestamp) return;
+      ttl = n->death_time - p->timestamp;
+    }
+
+    retip = n->ip;
+    unique = n->unique;
+
+    /* it may have been an earlier failure */
+    if (n->source == DNSFAIL) {
+      DEBUG(3,("DNSFAIL\n"));
+      return;
+    }
+  }
+
+  /* if the IP is 0 then substitute my IP - we should see which one is on the 
+     right interface for the caller to do this right XXX */
+  if (zero_ip(retip)) retip = myip;
+  
+  DEBUG(3,("OK %s rcode=%d\n",inet_ntoa(retip),rcode));      
+
+  /* a lot of fields get copied from the query. This gives us the IP
+     and port the reply will be sent to etc */
+  p2 = *p;
+  nmb2 = &p2.packet.nmb;
+
+  nmb2->header.response = True;
+  nmb2->header.nm_flags.bcast = False;
+  nmb2->header.nm_flags.recursion_available = CanRecurse;
+  nmb2->header.nm_flags.trunc = False;
+  nmb2->header.nm_flags.authoritative = True; /* WfWg ignores 
+                                                non-authoritative answers */
+  nmb2->header.qdcount = 0;
+  nmb2->header.ancount = 1;
+  nmb2->header.nscount = 0;
+  nmb2->header.arcount = 0;
+  nmb2->header.rcode = rcode;
+
+  nmb2->answers = &answer_rec;
+  bzero((char *)nmb2->answers,sizeof(*nmb2->answers));
+
+  nmb2->answers->rr_name = nmb->question.question_name;
+  nmb2->answers->rr_type = nmb->question.question_type;
+  nmb2->answers->rr_class = nmb->question.question_class;
+  nmb2->answers->ttl = ttl;
+  nmb2->answers->rdlength = 6;
+  nmb2->answers->rdata[0] = unique?0:0x80; 
+  nmb2->answers->rdata[1] = 0; 
+  putip(&nmb2->answers->rdata[2],(char *)&retip);
+
+  send_packet(&p2);
+}
+
+
+
+/* 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;
+
+
+/*******************************************************************
+  queue a packet into the 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;
+}
+
+/****************************************************************************
+  process a nmb packet
+  ****************************************************************************/
+static void process_nmb(struct packet_struct *p)
+{
+  struct nmb_packet *nmb = &p->packet.nmb;
+
+  /* if this is a response then ignore it */
+  if (nmb->header.response) return;
+
+  switch (nmb->header.opcode) 
+    {
+    case 5:
+    case 8:
+    case 9:
+      if (nmb->header.qdcount>0 && 
+         nmb->header.arcount>0) {
+       reply_name_reg(p);
+       return;
+      }
+      break;
+
+    case 0:
+      if (nmb->header.qdcount>0) 
+       {
+         switch (nmb->question.question_type)
+           {
+           case 0x20:
+             reply_name_query(p);
+             break;
+
+           case 0x21:
+             reply_name_status(p);
+             break;
+           }
+         return;
+       }
+      break;
+
+    case 6:
+      if (nmb->header.qdcount>0 && 
+         nmb->header.arcount>0) {
+       reply_name_release(p);
+       return;
+      }
+      break;
+    }
+
+}
+
+
+
+/*******************************************************************
+  run elements off the packet queue till its empty
+  ******************************************************************/
+static void run_packet_queue(void)
+{
+  struct packet_struct *p;
+
+  while ((p=packet_queue)) {
+    switch (p->packet_type)
+      {
+      case NMB_PACKET:
+       process_nmb(p);
+       break;
+
+      case DGRAM_PACKET:
+       process_dgram(p);
+       break;
+      }
+
+    packet_queue = packet_queue->next;
+    if (packet_queue) packet_queue->prev = NULL;
+    free_packet(p);
+  }
+}
+
+
+/****************************************************************************
+  The main select loop, listen for packets and respond
+  ***************************************************************************/
+void process(void)
+{
+
+  while (True)
+    {
+      fd_set fds;
+      int selrtn;
+      struct timeval timeout;
+
+      if (needelection && PrimaryGroup[0] && !RunningElection) {
+       DEBUG(3,(">>> Starting election on %s <<<\n",PrimaryGroup));
+       ElectionCount = 0;
+       RunningElection = True;
+       needelection = False;
+      }
+
+      FD_ZERO(&fds);
+      FD_SET(ClientNMB,&fds);
+      FD_SET(ClientDGRAM,&fds);
+      /* during elections we need to send election packets at one
+         second intervals */
+      timeout.tv_sec = RunningElection?1:NMBD_SELECT_LOOP;
+      timeout.tv_usec = 0;
+
+      selrtn = sys_select(&fds,&timeout);
+
+      if (FD_ISSET(ClientNMB,&fds)) {
+       struct packet_struct *packet = read_packet(ClientNMB,NMB_PACKET);
+       if (packet) queue_packet(packet);
+      }
+
+      if (FD_ISSET(ClientDGRAM,&fds)) {
+       struct packet_struct *packet = read_packet(ClientDGRAM,DGRAM_PACKET);
+       if (packet) queue_packet(packet);
+      }
+
+      if (RunningElection) 
+       run_election();
+
+      run_packet_queue();
+
+      do_announcements();
+
+      housekeeping();
+    }
+}
+
+
+/****************************************************************************
+  open the socket communication
+****************************************************************************/
+static BOOL open_sockets(BOOL isdaemon,int port)
+{
+  struct hostent *hp;
+  /* get host info */
+  if ((hp = Get_Hostbyname(myhostname)) == 0) 
+    {
+      DEBUG(0,( "Get_Hostbyname: Unknown host. %s\n",myhostname));
+      return False;
+    }   
+
+  if (isdaemon)
+    ClientNMB = open_socket_in(SOCK_DGRAM, port,0);
+  else
+    ClientNMB = 0;
+
+  ClientDGRAM = open_socket_in(SOCK_DGRAM,DGRAM_PORT,3);
+
+  if (ClientNMB == -1)
+    return(False);
+
+  signal(SIGPIPE, SIGNAL_CAST sig_pipe);
+
+  set_socket_options(ClientNMB,"SO_BROADCAST");
+  set_socket_options(ClientDGRAM,"SO_BROADCAST");
+
+  DEBUG(3, ("Socket opened.\n"));
+  return True;
+}
+
+
+/*******************************************************************
+  check that a IP, bcast and netmask and consistent. Must be a 1s
+  broadcast
+  ******************************************************************/
+static BOOL ip_consistent(struct in_addr ip,struct in_addr bcast,
+                         struct in_addr nmask)
+{
+  unsigned long a_ip,a_bcast,a_nmask;
+
+  a_ip = ntohl(ip.s_addr);
+  a_bcast = ntohl(bcast.s_addr);
+  a_nmask = ntohl(nmask.s_addr);
+
+  /* check the netmask is sane */
+  if (((a_nmask>>24)&0xFF) != 0xFF) {
+    DEBUG(0,("Insane netmask %s\n",inet_ntoa(nmask)));
+    return(False);
+  }
+
+  /* check the IP and bcast are on the same net */
+  if ((a_ip&a_nmask) != (a_bcast&a_nmask)) {
+    DEBUG(0,("IP and broadcast are on different nets!\n"));
+    return(False);
+  }
+
+  /* check the IP and bcast are on the same net */
+  if ((a_bcast|a_nmask) != 0xFFFFFFFF) {
+    DEBUG(0,("Not a ones based broadcast %s\n",inet_ntoa(bcast)));
+    return(False);
+  }
+
+  return(True);
+}
+
+/****************************************************************************
+  initialise connect, service and file structs
+****************************************************************************/
+static BOOL init_structs(void )
+{
+  if (!get_myname(myhostname,got_myip?NULL:&myip))
+    return(False);
+
+  /* Read the broadcast address from the interface */
+  {
+    struct in_addr ip0,ip1,ip2;
+
+    ip0 = myip;
+
+    if (!(got_bcast && got_nmask))
+      {
+       get_broadcast(&ip0,&ip1,&ip2);
+
+       if (!got_myip)
+         myip = ip0;
+    
+       if (!got_bcast)
+         bcast_ip = ip1;
+    
+       if (!got_nmask)
+         Netmask = ip2;   
+      } 
+
+    DEBUG(1,("Using IP %s  ",inet_ntoa(myip))); 
+    DEBUG(1,("broadcast %s  ",inet_ntoa(bcast_ip)));
+    DEBUG(1,("netmask %s\n",inet_ntoa(Netmask)));    
+
+    if (!ip_consistent(myip,bcast_ip,Netmask)) {
+      DEBUG(0,("WARNING: The IP address, broadcast and Netmask are not consistent\n"));
+      DEBUG(0,("You are likely to experience problems with this setup!\n"));
+    }
+  }
+
+  if (! *myname) {
+    char *p;
+    strcpy(myname,myhostname);
+    p = strchr(myname,'.');
+    if (p) *p = 0;
+  }
+
+  {
+    extern fstring local_machine;
+    strcpy(local_machine,myname);
+    strupper(local_machine);
+  }
+
+  return True;
+}
+
+/****************************************************************************
+usage on the program
+****************************************************************************/
+static void usage(char *pname)
+{
+  DEBUG(0,("Incorrect program usage - is the command line correct?\n"));
+
+  printf("Usage: %s [-n name] [-B bcast address] [-D] [-p port] [-d debuglevel] [-l log basename]\n",pname);
+  printf("Version %s\n",VERSION);
+  printf("\t-D                    become a daemon\n");
+  printf("\t-p port               listen on the specified port\n");
+  printf("\t-d debuglevel         set the debuglevel\n");
+  printf("\t-l log basename.      Basename for log/debug files\n");
+  printf("\t-n netbiosname.       the netbios name to advertise for this host\n");
+  printf("\t-B broadcast address  the address to use for broadcasts\n");
+  printf("\t-N netmask           the netmask to use for subnet determination\n");
+  printf("\t-H hosts file        load a netbios hosts file\n");
+  printf("\t-I ip-address        override the IP address\n");
+  printf("\t-G group name        add a group name to be part of\n");
+  printf("\t-C comment           sets the machine comment that appears in browse lists\n");
+  printf("\n");
+}
+
+
+/****************************************************************************
+  main program
+  **************************************************************************/
+int main(int argc,char *argv[])
+{
+  int port = NMB_PORT;
+  int opt;
+  extern FILE *dbf;
+  extern char *optarg;
+
+  *host_file = 0;
+
+#if 0
+  sleep(10);
+#endif
+
+  StartupTime = time(NULL);
+
+  TimeInit();
+
+  strcpy(debugf,NMBLOGFILE);
+
+  setup_logging(argv[0],False);
+
+  charset_initialise();
+
+#ifdef LMHOSTSFILE
+  strcpy(host_file,LMHOSTSFILE);
+#endif
+
+  /* this is for people who can't start the program correctly */
+  while (argc > 1 && (*argv[1] != '-'))
+    {
+      argv++;
+      argc--;
+    }
+
+  fault_setup(fault_continue);
+
+  signal(SIGHUP,SIGNAL_CAST sig_hup);
+
+  bcast_ip = *interpret_addr2("0.0.0.0");
+  myip = *interpret_addr2("0.0.0.0");
+
+  while ((opt = getopt (argc, argv, "s:T:I:C:bAi:B:N:Rn:l:d:Dp:hSH:G:")) != EOF)
+    switch (opt)
+      {
+      case 's':
+       strcpy(servicesf,optarg);
+       break;
+      case 'C':
+       strcpy(ServerComment,optarg);
+       break;
+      case 'G':
+       add_domain_entry(optarg,bcast_ip);
+       break;
+      case 'H':
+       strcpy(host_file,optarg);
+       break;
+      case 'I':
+       myip = *interpret_addr2(optarg);
+       got_myip = True;
+       break;
+      case 'B':
+       bcast_ip = *interpret_addr2(optarg);
+       got_bcast = True;
+       break;
+      case 'N':
+       Netmask = *interpret_addr2(optarg);
+       got_nmask = True;
+       break;
+      case 'n':
+       strcpy(myname,optarg);
+       break;
+      case 'l':
+       sprintf(debugf,"%s.nmb",optarg);
+       break;
+      case 'i':
+       strcpy(scope,optarg);
+       strupper(scope);
+       break;
+      case 'D':
+       is_daemon = True;
+       break;
+      case 'd':
+       DEBUGLEVEL = atoi(optarg);
+       break;
+      case 'p':
+       port = atoi(optarg);
+       break;
+      case 'h':
+       usage(argv[0]);
+       exit(0);
+       break;
+      default:
+       if (!is_a_socket(0))
+         usage(argv[0]);
+       break;
+      }
+  
+  DEBUG(1,("%s netbios nameserver version %s started\n",timestring(),VERSION));
+  DEBUG(1,("Copyright Andrew Tridgell 1994\n"));
+
+  init_structs();
+
+  if (!reload_services(False))
+    return(-1);        
+
+  if (*host_file)
+    {
+      load_hosts_file(host_file);
+      DEBUG(3,("Loaded hosts file\n"));
+    }
+
+  if (!*ServerComment)
+    strcpy(ServerComment,"Samba %v");
+  string_sub(ServerComment,"%v",VERSION);
+  string_sub(ServerComment,"%h",myhostname);
+
+  add_my_names();
+
+  DEBUG(3,("Checked names\n"));
+  
+  dump_names();
+
+  DEBUG(3,("Dumped names\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) {
+    DEBUG(2,("%s becoming a daemon\n",timestring()));
+    become_daemon();
+  }
+
+
+  DEBUG(3,("Opening sockets\n"));
+
+  if (open_sockets(is_daemon,port))
+    {
+      process();
+      close_sockets();
+    }
+
+  if (dbf)
+    fclose(dbf);
+  return(0);
+}
diff --git a/source/nmbsync.c b/source/nmbsync.c
new file mode 100644 (file)
index 0000000..5a77d6c
--- /dev/null
@@ -0,0 +1,303 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   NBT netbios routines to synchronise browse lists
+   Copyright (C) Andrew Tridgell 1994-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"
+#include "loadparm.h"
+#include "nameserv.h"
+
+extern int DEBUGLEVEL;
+
+struct server_record *add_server_entry(char *name,int servertype,
+                                      int ttl,char *comment,BOOL replace);
+
+
+/****************************************************************************
+call a remote api
+****************************************************************************/
+static BOOL call_remote_api(int fd,int cnum,int uid,int timeout,
+                           char *inbuf,char *outbuf,
+                           int prcnt,int drcnt,
+                           int mprcnt,int mdrcnt,
+                           int *rprcnt,int *rdrcnt,
+                           char *param,char *data,
+                           char **rparam,char **rdata)
+{
+  char *p1,*p2;
+
+  /* send a SMBtrans command */
+  bzero(outbuf,smb_size);
+  set_message(outbuf,14,0,True);
+  CVAL(outbuf,smb_com) = SMBtrans;
+  SSVAL(outbuf,smb_tid,cnum);
+  SSVAL(outbuf,smb_uid,uid);
+
+  p1 = smb_buf(outbuf);
+  strcpy(p1,"\\PIPE\\LANMAN");
+  p1 = skip_string(p1,1);
+  p2 = p1 + prcnt;
+
+  if (prcnt > 0)
+    memcpy(p1,param,prcnt);
+  if (drcnt > 0)
+    memcpy(p2,data,drcnt);
+
+  SSVAL(outbuf,smb_vwv0,prcnt); /* param count */
+  SSVAL(outbuf,smb_vwv1,drcnt); /* data count */
+  SSVAL(outbuf,smb_vwv2,mprcnt); /* mprcnt */
+  SSVAL(outbuf,smb_vwv3,mdrcnt); /* mdrcnt */
+  SSVAL(outbuf,smb_vwv4,0); /* msrcnt */
+  SSVAL(outbuf,smb_vwv5,0); /* flags */
+  SSVAL(outbuf,smb_vwv9,prcnt); /* pscnt */
+  SSVAL(outbuf,smb_vwv10,smb_offset(p1,outbuf)); /* psoff */
+  SSVAL(outbuf,smb_vwv11,drcnt); /* dscnt */
+  SSVAL(outbuf,smb_vwv12,smb_offset(p2,outbuf)); /* dsoff */
+  CVAL(outbuf,smb_vwv13) = 0; /* suwcnt */
+
+  set_message(outbuf,14,PTR_DIFF(p2+drcnt,smb_buf(outbuf)),False);
+
+  send_smb(fd,outbuf);
+  
+  if (receive_smb(fd,inbuf,timeout) &&
+      CVAL(inbuf,smb_rcls) == 0)
+    {
+      if (rparam)
+       *rparam = inbuf+4 + SVAL(inbuf,smb_vwv4);
+      if (rdata)
+       *rdata = inbuf+4 + SVAL(inbuf,smb_vwv7);
+      if (rprcnt)
+       *rprcnt = SVAL(inbuf,smb_vwv3);
+      if (rdrcnt)
+       *rdrcnt = SVAL(inbuf,smb_vwv6);
+      return(True);
+    }
+
+  return(False);
+}
+
+
+/*******************************************************************
+  synchronise browse lists with another browse server
+  ******************************************************************/
+void sync_browse_lists(char *name,int name_type,char *myname,
+                      char *domain,struct in_addr ip)
+{
+  char *protocol = "LM1.2X002";
+  char *service = "IPC$";
+  char *dev = "IPC";
+  int timeout=2000;
+  char *inbuf=NULL;
+  pstring outbuf;
+  char *p;
+  int len;
+  uint32 sesskey;
+  int cnum,uid;
+  BOOL ret;
+
+  int fd = open_socket_out(SOCK_STREAM, &ip, SMB_PORT);
+  if (fd < 0) {
+    DEBUG(3,("Failed to connect to %s at %s\n",name,inet_ntoa(ip)));
+    return;
+  }
+
+  if (!(inbuf = (char *)malloc(0xFFFF+1024))) return;  
+
+  /* put in the destination name */
+  len = 4;
+  p = outbuf+len;
+  name_mangle(name,p,name_type);
+  len += name_len(p);
+
+  /* and my name */
+  p = outbuf+len;
+  name_mangle(myname,p,0x20);
+  len += name_len(p);
+
+  _smb_setlen(outbuf,len);
+  CVAL(outbuf,0) = 0x81;
+
+  send_smb(fd,outbuf);
+  receive_smb(fd,inbuf,5000);
+  
+  bzero(outbuf,smb_size);
+
+  /* setup the protocol string */
+  set_message(outbuf,0,strlen(protocol)+2,True);
+  p = smb_buf(outbuf);
+  *p++ = 2;
+  strcpy(p,protocol);
+
+  CVAL(outbuf,smb_com) = SMBnegprot;
+  CVAL(outbuf,smb_flg) = 0x8;
+  SSVAL(outbuf,smb_flg2,0x1);
+
+  send_smb(fd,outbuf);
+  bzero(inbuf,smb_size);
+  ret = receive_smb(fd,inbuf,timeout);
+  
+  if (!ret || CVAL(inbuf,smb_rcls) || SVAL(inbuf,smb_vwv0)) {
+    DEBUG(3,("%s rejected the protocol\n",name));
+    close(fd);
+    if (inbuf) free(inbuf);
+    return;
+  }
+
+  sesskey = IVAL(inbuf,smb_vwv6);
+
+  bzero(outbuf,smb_size);
+  set_message(outbuf,10,2,True);
+  CVAL(outbuf,smb_com) = SMBsesssetupX;
+
+  CVAL(outbuf,smb_vwv0) = 0xFF;
+  SSVAL(outbuf,smb_vwv2,0xFFFF);
+  SSVAL(outbuf,smb_vwv3,2);
+  SSVAL(outbuf,smb_vwv4,1);
+  SIVAL(outbuf,smb_vwv5,sesskey);
+  SSVAL(outbuf,smb_vwv7,1);
+
+  send_smb(fd,outbuf);
+  bzero(inbuf,smb_size);
+  ret = receive_smb(fd,inbuf,timeout);
+  if (!ret || CVAL(inbuf,smb_rcls)) {
+    DEBUG(3,("%s rejected session setup\n",name));
+    close(fd);
+    if (inbuf) free(inbuf);
+    return;
+  }
+
+  uid = SVAL(inbuf,smb_uid);
+
+  bzero(outbuf,smb_size);
+  set_message(outbuf,4,2 + (2 + strlen(name) + 1 + strlen(service)) +
+       1 + strlen(dev),True);
+  CVAL(outbuf,smb_com) = SMBtconX;
+  SSVAL(outbuf,smb_uid,uid);
+
+  SSVAL(outbuf,smb_vwv0,0xFF);
+  SSVAL(outbuf,smb_vwv3,1);
+
+  p = smb_buf(outbuf) + 1;
+  strcpy(p, "\\\\");
+  strcat(p, name);
+  strcat(p, "\\");
+  strcat(p,service);
+  p = skip_string(p,1);
+  strcpy(p,dev);
+
+  send_smb(fd,outbuf);
+  bzero(inbuf,smb_size);
+  ret = receive_smb(fd,inbuf,timeout);
+  if (!ret || CVAL(inbuf,smb_rcls)) {
+    DEBUG(3,("%s rejected IPC connect (%d,%d)\n",name,
+            CVAL(inbuf,smb_rcls),SVAL(inbuf,smb_err)));
+    close(fd);
+    if (inbuf) free(inbuf);
+    return;
+  }
+
+  cnum = SVAL(inbuf,smb_tid);
+  
+  /* now I need to send a NetServerEnum */
+  {
+    fstring param;
+    uint32 *typep;
+    char *rparam,*rdata;
+
+    p = param;
+    SSVAL(p,0,0x68); /* api number */
+    p += 2;
+    strcpy(p,"WrLehDz");
+    p = skip_string(p,1);
+
+    strcpy(p,"B16BBDz");
+
+    p = skip_string(p,1);
+    SSVAL(p,0,1); /* level 1 */
+    SSVAL(p,2,0xFFFF - 500); /* buf length */
+    p += 4;
+    typep = (uint32 *)p;
+    p += 4;
+    strcpy(p,domain);
+    strupper(p);
+    p = skip_string(p,1);
+
+    SIVAL(typep,0,0x80000000); /* domain list */
+
+    if (call_remote_api(fd,cnum,uid,timeout,inbuf,outbuf,
+                       PTR_DIFF(p,param),0,
+                       8,0xFFFF - 500,
+                       NULL,NULL,
+                       param,NULL,
+                       &rparam,&rdata) && SVAL(rparam,0)==0)
+    {
+      int converter=SVAL(rparam,2);
+      int count=SVAL(rparam,4);
+      int i;
+      char *p2 = rdata;
+      for (i=0;i<count;i++) {
+       char *sname = p2;
+       uint32 type = IVAL(p2,18);
+       int comment_offset = IVAL(p2,22) & 0xFFFF;
+       char *comment = comment_offset?(rdata+comment_offset-converter):"";
+
+       add_server_entry(sname,type,lp_max_ttl(),comment,False);
+       p2 += 26;
+      }
+    }
+
+    SIVAL(typep,0,0xFFFFFFFF); /* server list */
+
+    if (call_remote_api(fd,cnum,uid,timeout,inbuf,outbuf,
+                       PTR_DIFF(p,param),0,
+                       8,0xFFFF - 500,
+                       NULL,NULL,
+                       param,NULL,
+                       &rparam,&rdata) && SVAL(rparam,0)==0)
+    {
+      int converter=SVAL(rparam,2);
+      int count=SVAL(rparam,4);
+      int i;
+
+      p = rdata;
+      for (i=0;i<count;i++) {
+       char *sname = p;
+       uint32 type = IVAL(p,18);
+       int comment_offset = IVAL(p,22) & 0xFFFF;
+       char *comment = comment_offset?(rdata+comment_offset-converter):"";
+
+       add_server_entry(sname,type,lp_max_ttl(),comment,False);
+       p += 26;
+      }
+    }
+  }
+
+  /* close up */
+  bzero(outbuf,smb_size);
+  set_message(outbuf,0,0,True);
+  CVAL(outbuf,smb_com) = SMBtdis;
+  SSVAL(outbuf,smb_uid,uid);
+  SSVAL(outbuf,smb_tid,cnum);
+  send_smb(fd,outbuf);
+  receive_smb(fd,inbuf,1000);
+  
+  close(fd);
+  if (inbuf) free(inbuf);
+}
diff --git a/source/param/loadparm.c b/source/param/loadparm.c
new file mode 100644 (file)
index 0000000..c61ab26
--- /dev/null
@@ -0,0 +1,1891 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   Parameter loading functions
+   Copyright (C) Karl Auer 1993,1994
+
+   Largely re-written by Andrew Tridgell, September 1994
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the 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"
+
+#include "params.h"
+#include "loadparm.h"
+#include "pcap.h"
+
+BOOL bLoaded = False;
+
+extern int DEBUGLEVEL;
+extern int ReadSize;
+extern pstring user_socket_options;
+extern pstring smbrun_path;
+
+#ifndef GLOBAL_NAME
+#define GLOBAL_NAME "global"
+#endif
+
+#ifndef PRINTCAP_NAME
+#ifdef AIX
+#define PRINTCAP_NAME "/etc/qconfig"
+#else
+#define PRINTCAP_NAME "/etc/printcap"
+#endif
+#endif
+
+#ifndef PRINTERS_NAME
+#define PRINTERS_NAME "printers"
+#endif
+
+#ifndef HOMES_NAME
+#define HOMES_NAME "homes"
+#endif
+
+/* some helpful bits */
+#define pSERVICE(i) ServicePtrs[i]
+#define iSERVICE(i) (*pSERVICE(i))
+#define LP_SNUM_OK(iService) (((iService) >= 0) && ((iService) < iNumServices) && iSERVICE(iService).valid)
+#define VALID(i) iSERVICE(i).valid
+
+/* these are the types of parameter we have */
+typedef enum
+{
+  P_BOOL,P_BOOLREV,P_CHAR,P_INTEGER,P_OCTAL,P_STRING,P_GSTRING
+} parm_type;
+
+typedef enum
+{
+  P_LOCAL,P_GLOBAL,P_NONE
+} parm_class;
+
+int keepalive=0;
+extern BOOL use_getwd_cache;
+
+extern int extra_time_offset;
+#ifdef KANJI
+extern int coding_system;
+#endif
+
+/* 
+ * This structure describes global (ie., server-wide) parameters.
+ */
+typedef struct
+{
+   char *szPrintcapname;
+   char *szLockDir;
+   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 *szPasswordServer;
+   char *szSocketOptions;
+   char *szValidChars;
+   char *szWorkGroup;
+   char *szDomainController;
+   char *szUsernameMap;
+   char *szCharacterSet;
+   char *szLogonScript;
+   int max_log_size;
+   int mangled_stack;
+   int max_xmit;
+   int max_mux;
+   int max_packet;
+   int pwordlevel;
+   int deadtime;
+   int maxprotocol;
+   int security;
+   int printing;
+   int maxdisksize;
+   int lpqcachetime;
+   int syslog;
+   int os_level;
+   int max_ttl;
+   BOOL bPreferredMaster;
+   BOOL bDomainMaster;
+   BOOL bDomainLogons;
+   BOOL bEncryptPasswords;
+   BOOL bStripDot;
+   BOOL bNullPasswords;
+   BOOL bLoadPrinters;
+   BOOL bUseRhosts;
+   BOOL bReadRaw;
+   BOOL bWriteRaw;
+   BOOL bReadPrediction;
+   BOOL bReadbmpx;
+   BOOL bSyslogOnly;
+   BOOL bBrowseList;
+} global;
+
+static global Globals;
+
+
+
+/* 
+ * This structure describes a single service. 
+ */
+typedef struct
+{
+  BOOL valid;
+  char *szService;
+  char *szPath;
+  char *szUsername;
+  char *szGuestaccount;
+  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 *szPrintername;
+  char *szDontdescend;
+  char *szHostsallow;
+  char *szHostsdeny;
+  char *szMagicScript;
+  char *szMagicOutput;
+  char *szMangledMap;
+  char *comment;
+  char *force_user;
+  char *force_group;
+  char *readlist;
+  char *writelist;
+  char *volume;
+  int  iMinPrintSpace;
+  int  iCreate_mode;
+  int  iMaxConnections;
+  int  iDefaultCase;
+  BOOL bAlternatePerm;
+  BOOL bRevalidate;
+  BOOL bCaseSensitive;
+  BOOL bCasePreserve;
+  BOOL bShortCasePreserve;
+  BOOL bCaseMangle;
+  BOOL status;
+  BOOL bHideDotFiles;
+  BOOL bBrowseable;
+  BOOL bAvailable;
+  BOOL bRead_only;
+  BOOL bNo_set_dir;
+  BOOL bGuest_only;
+  BOOL bGuest_ok;
+  BOOL bPrint_ok;
+  BOOL bPostscript;
+  BOOL bMap_system;
+  BOOL bMap_hidden;
+  BOOL bMap_archive;
+  BOOL bLocking;
+  BOOL bStrictLocking;
+  BOOL bShareModes;
+  BOOL bOnlyUser;
+  BOOL bMangledNames;
+  BOOL bWidelinks;
+  BOOL bSyncAlways;
+  char magic_char;
+  BOOL *copymap;
+  char dummy[3]; /* for alignment */
+} service;
+
+
+/* This is a default service used to prime a services structure */
+static service sDefault = 
+{
+  True,   /* valid */
+  NULL,    /* szService */
+  NULL,    /* szPath */
+  NULL,    /* szUsername */
+  NULL,    /* szGuestAccount */
+  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,    /* szPrintername */
+  NULL,    /* szDontdescend */
+  NULL,    /* szHostsallow */
+  NULL,    /* szHostsdeny */
+  NULL,    /* szMagicScript */
+  NULL,    /* szMagicOutput */
+  NULL,    /* szMangledMap */
+  NULL,    /* comment */
+  NULL,    /* force user */
+  NULL,    /* force group */
+  NULL,    /* readlist */
+  NULL,    /* writelist */
+  NULL,    /* volume */
+  0,       /* iMinPrintSpace */
+  0755,    /* iCreate_mode */
+  0,       /* iMaxConnections */
+  CASE_LOWER, /* iDefaultCase */
+  False,   /* bAlternatePerm */
+  False,   /* revalidate */
+  False,   /* case sensitive */
+  False,   /* case preserve */
+  False,   /* short case preserve */
+  False,  /* case mangle */
+  True,  /* status */
+  True,  /* bHideDotFiles */
+  True,  /* bBrowseable */
+  True,  /* bAvailable */
+  True,  /* bRead_only */
+  True,  /* bNo_set_dir */
+  False, /* bGuest_only */
+  False, /* bGuest_ok */
+  False, /* bPrint_ok */
+  False, /* bPostscript */
+  False, /* bMap_system */
+  False, /* bMap_hidden */
+  True,  /* bMap_archive */
+  True,  /* bLocking */
+  False,  /* bStrictLocking */
+  True,  /* bShareModes */
+  False, /* bOnlyUser */
+  True,  /* bMangledNames */
+  True,  /* bWidelinks */
+  False, /* bSyncAlways */
+  '~',   /* magic char */
+  NULL,  /* copymap */
+  ""     /* dummy */
+};
+
+
+
+/* local variables */
+static service **ServicePtrs = NULL;
+static int iNumServices = 0;
+static int iServiceIndex = 0;
+static BOOL bInGlobalSection = True;
+static BOOL bGlobalOnly = False;
+
+
+#define NUMPARAMETERS (sizeof(parm_table) / sizeof(struct parm_struct))
+
+/* prototypes for the special type handlers */
+static BOOL handle_valid_chars(char *pszParmValue, char **ptr);
+static BOOL handle_include(char *pszParmValue, char **ptr);
+static BOOL handle_copy(char *pszParmValue, char **ptr);
+static BOOL handle_protocol(char *pszParmValue,int *val);
+static BOOL handle_security(char *pszParmValue,int *val);
+static BOOL handle_case(char *pszParmValue,int *val);
+static BOOL handle_printing(char *pszParmValue,int *val);
+static BOOL handle_character_set(char *pszParmValue,int *val);
+#ifdef KANJI
+static BOOL handle_coding_system(char *pszParmValue,int *val);
+#endif /* KANJI */
+
+struct parm_struct
+{
+  char *label;
+  parm_type type;
+  parm_class class;
+  void *ptr;
+  BOOL (*special)();
+} parm_table[] =
+{
+  {"debuglevel",       P_INTEGER, P_GLOBAL, &DEBUGLEVEL,                NULL},
+  {"log level",        P_INTEGER, P_GLOBAL, &DEBUGLEVEL,                NULL},
+  {"syslog",           P_INTEGER, P_GLOBAL, &Globals.syslog,            NULL},
+  {"syslog only",      P_BOOL,    P_GLOBAL, &Globals.bSyslogOnly,       NULL},
+  {"protocol",         P_INTEGER, P_GLOBAL, &Globals.maxprotocol,handle_protocol},
+  {"security",         P_INTEGER, P_GLOBAL, &Globals.security,handle_security},
+  {"printing",         P_INTEGER, P_GLOBAL, &Globals.printing,handle_printing},
+  {"max disk size",    P_INTEGER, P_GLOBAL, &Globals.maxdisksize,       NULL},
+  {"lpq cache time",   P_INTEGER, P_GLOBAL, &Globals.lpqcachetime,      NULL},
+  {"encrypt passwords",P_BOOL,    P_GLOBAL, &Globals.bEncryptPasswords, NULL},
+  {"getwd cache",      P_BOOL,    P_GLOBAL, &use_getwd_cache,           NULL},
+  {"read prediction",  P_BOOL,    P_GLOBAL, &Globals.bReadPrediction,   NULL},
+  {"read bmpx",        P_BOOL,    P_GLOBAL, &Globals.bReadbmpx,         NULL},
+  {"read raw",         P_BOOL,    P_GLOBAL, &Globals.bReadRaw,          NULL},
+  {"write raw",        P_BOOL,    P_GLOBAL, &Globals.bWriteRaw,         NULL},
+  {"use rhosts",       P_BOOL,    P_GLOBAL, &Globals.bUseRhosts,        NULL},
+  {"load printers",    P_BOOL,    P_GLOBAL, &Globals.bLoadPrinters,     NULL},
+  {"null passwords",   P_BOOL,    P_GLOBAL, &Globals.bNullPasswords,    NULL},
+  {"strip dot",        P_BOOL,    P_GLOBAL, &Globals.bStripDot,         NULL},
+  {"password server",  P_STRING,  P_GLOBAL, &Globals.szPasswordServer,  NULL},
+  {"socket options",   P_GSTRING, P_GLOBAL, user_socket_options,        NULL},
+  {"smbrun",           P_GSTRING, P_GLOBAL, smbrun_path,                NULL},
+  {"log file",         P_STRING,  P_GLOBAL, &Globals.szLogFile,         NULL},
+  {"config file",      P_STRING,  P_GLOBAL, &Globals.szConfigFile,      NULL},
+  {"smb passwd file",  P_STRING,  P_GLOBAL, &Globals.szSMBPasswdFile,   NULL},
+  {"hosts equiv",      P_STRING,  P_GLOBAL, &Globals.szHostsEquiv,      NULL},
+  {"preload",          P_STRING,  P_GLOBAL, &Globals.szAutoServices,    NULL},
+  {"auto services",    P_STRING,  P_GLOBAL, &Globals.szAutoServices,    NULL},
+  {"server string",    P_STRING,  P_GLOBAL, &Globals.szServerString,    NULL},
+  {"printcap name",    P_STRING,  P_GLOBAL, &Globals.szPrintcapname,    NULL},
+  {"printcap",         P_STRING,  P_GLOBAL, &Globals.szPrintcapname,    NULL},
+  {"lock dir",         P_STRING,  P_GLOBAL, &Globals.szLockDir,         NULL},
+  {"lock directory",   P_STRING,  P_GLOBAL, &Globals.szLockDir,         NULL},
+  {"root directory",   P_STRING,  P_GLOBAL, &Globals.szRootdir,         NULL},
+  {"root dir",         P_STRING,  P_GLOBAL, &Globals.szRootdir,         NULL},
+  {"root",             P_STRING,  P_GLOBAL, &Globals.szRootdir,         NULL},
+  {"default service",  P_STRING,  P_GLOBAL, &Globals.szDefaultService,  NULL},
+  {"default",          P_STRING,  P_GLOBAL, &Globals.szDefaultService,  NULL},
+  {"message command",  P_STRING,  P_GLOBAL, &Globals.szMsgCommand,      NULL},
+  {"dfree command",    P_STRING,  P_GLOBAL, &Globals.szDfree,           NULL},
+  {"passwd program",   P_STRING,  P_GLOBAL, &Globals.szPasswdProgram,   NULL},
+  {"passwd chat",      P_STRING,  P_GLOBAL, &Globals.szPasswdChat,      NULL},
+  {"valid chars",      P_STRING,  P_GLOBAL, &Globals.szValidChars,      handle_valid_chars},
+  {"workgroup",        P_STRING,  P_GLOBAL, &Globals.szWorkGroup,       NULL},
+  {"domain controller",P_STRING,  P_GLOBAL, &Globals.szDomainController,NULL},
+  {"username map",     P_STRING,  P_GLOBAL, &Globals.szUsernameMap,     NULL},
+  {"character set",    P_STRING,  P_GLOBAL, &Globals.szCharacterSet,    handle_character_set},
+  {"logon script",     P_STRING,  P_GLOBAL, &Globals.szLogonScript,     NULL},
+  {"max log size",     P_INTEGER, P_GLOBAL, &Globals.max_log_size,      NULL},
+  {"mangled stack",    P_INTEGER, P_GLOBAL, &Globals.mangled_stack,     NULL},
+  {"max mux",          P_INTEGER, P_GLOBAL, &Globals.max_mux,           NULL},
+  {"max xmit",         P_INTEGER, P_GLOBAL, &Globals.max_xmit,          NULL},
+  {"max packet",       P_INTEGER, P_GLOBAL, &Globals.max_packet,        NULL},
+  {"packet size",      P_INTEGER, P_GLOBAL, &Globals.max_packet,        NULL},
+  {"password level",   P_INTEGER, P_GLOBAL, &Globals.pwordlevel,        NULL},
+  {"keepalive",        P_INTEGER, P_GLOBAL, &keepalive,                 NULL},
+  {"deadtime",         P_INTEGER, P_GLOBAL, &Globals.deadtime,          NULL},
+  {"time offset",      P_INTEGER, P_GLOBAL, &extra_time_offset,         NULL},
+  {"read size",        P_INTEGER, P_GLOBAL, &ReadSize,                  NULL},
+#ifdef KANJI
+  {"coding system",    P_INTEGER, P_GLOBAL, &coding_system, handle_coding_system},
+#endif /* KANJI */
+  {"os level",         P_INTEGER, P_GLOBAL, &Globals.os_level,          NULL},
+  {"max ttl",          P_INTEGER, P_GLOBAL, &Globals.max_ttl,           NULL},
+  {"preferred master", P_BOOL,    P_GLOBAL, &Globals.bPreferredMaster,  NULL},
+  {"prefered master",  P_BOOL,    P_GLOBAL, &Globals.bPreferredMaster,  NULL},
+  {"domain master",    P_BOOL,    P_GLOBAL, &Globals.bDomainMaster,     NULL},
+  {"domain logons",    P_BOOL,    P_GLOBAL, &Globals.bDomainLogons,     NULL},
+  {"browse list",      P_BOOL,    P_GLOBAL, &Globals.bBrowseList,       NULL},
+
+  {"-valid",           P_BOOL,    P_LOCAL,  &sDefault.valid,            NULL},
+  {"comment",          P_STRING,  P_LOCAL,  &sDefault.comment,          NULL},
+  {"copy",             P_STRING,  P_LOCAL,  &sDefault.szCopy,    handle_copy},
+  {"include",          P_STRING,  P_LOCAL,  &sDefault.szInclude, handle_include},
+  {"exec",             P_STRING,  P_LOCAL,  &sDefault.szPreExec,        NULL},
+  {"preexec",          P_STRING,  P_LOCAL,  &sDefault.szPreExec,        NULL},
+  {"postexec",         P_STRING,  P_LOCAL,  &sDefault.szPostExec,       NULL},
+  {"root preexec",     P_STRING,  P_LOCAL,  &sDefault.szRootPreExec,    NULL},
+  {"root postexec",    P_STRING,  P_LOCAL,  &sDefault.szRootPostExec,   NULL},
+  {"alternate permissions",P_BOOL,P_LOCAL,  &sDefault.bAlternatePerm,   NULL},
+  {"revalidate",       P_BOOL,    P_LOCAL,  &sDefault.bRevalidate,      NULL},
+  {"default case",     P_INTEGER, P_LOCAL,  &sDefault.iDefaultCase,   handle_case},
+  {"case sensitive",   P_BOOL,    P_LOCAL,  &sDefault.bCaseSensitive,   NULL},
+  {"casesignames",     P_BOOL,    P_LOCAL,  &sDefault.bCaseSensitive,   NULL},
+  {"preserve case",    P_BOOL,    P_LOCAL,  &sDefault.bCasePreserve,    NULL},
+  {"short preserve case",P_BOOL,  P_LOCAL,  &sDefault.bShortCasePreserve,NULL},
+  {"mangle case",      P_BOOL,    P_LOCAL,  &sDefault.bCaseMangle,      NULL},
+  {"mangling char",    P_CHAR,    P_LOCAL,  &sDefault.magic_char,       NULL},
+  {"browseable",       P_BOOL,    P_LOCAL,  &sDefault.bBrowseable,      NULL},
+  {"browsable",        P_BOOL,    P_LOCAL,  &sDefault.bBrowseable,      NULL},
+  {"available",        P_BOOL,    P_LOCAL,  &sDefault.bAvailable,       NULL},
+  {"path",             P_STRING,  P_LOCAL,  &sDefault.szPath,           NULL},
+  {"directory",        P_STRING,  P_LOCAL,  &sDefault.szPath,           NULL},
+  {"username",         P_STRING,  P_LOCAL,  &sDefault.szUsername,       NULL},
+  {"user",             P_STRING,  P_LOCAL,  &sDefault.szUsername,       NULL},
+  {"users",            P_STRING,  P_LOCAL,  &sDefault.szUsername,       NULL},
+  {"guest account",    P_STRING,  P_LOCAL,  &sDefault.szGuestaccount,   NULL},
+  {"invalid users",    P_STRING,  P_LOCAL,  &sDefault.szInvalidUsers,   NULL},
+  {"valid users",      P_STRING,  P_LOCAL,  &sDefault.szValidUsers,     NULL},
+  {"admin users",      P_STRING,  P_LOCAL,  &sDefault.szAdminUsers,     NULL},
+  {"read list",        P_STRING,  P_LOCAL,  &sDefault.readlist,         NULL},
+  {"write list",       P_STRING,  P_LOCAL,  &sDefault.writelist,        NULL},
+  {"volume",           P_STRING,  P_LOCAL,  &sDefault.volume,           NULL},
+  {"force user",       P_STRING,  P_LOCAL,  &sDefault.force_user,       NULL},
+  {"force group",      P_STRING,  P_LOCAL,  &sDefault.force_group,      NULL},
+  {"group",            P_STRING,  P_LOCAL,  &sDefault.force_group,      NULL},
+  {"read only",        P_BOOL,    P_LOCAL,  &sDefault.bRead_only,       NULL},
+  {"write ok",         P_BOOLREV, P_LOCAL,  &sDefault.bRead_only,       NULL},
+  {"writeable",        P_BOOLREV, P_LOCAL,  &sDefault.bRead_only,       NULL},
+  {"writable",         P_BOOLREV, P_LOCAL,  &sDefault.bRead_only,       NULL},
+  {"max connections",  P_INTEGER, P_LOCAL,  &sDefault.iMaxConnections,  NULL},
+  {"min print space",  P_INTEGER, P_LOCAL,  &sDefault.iMinPrintSpace,   NULL},
+  {"create mask",      P_OCTAL,   P_LOCAL,  &sDefault.iCreate_mode,     NULL},
+  {"create mode",      P_OCTAL,   P_LOCAL,  &sDefault.iCreate_mode,     NULL},
+  {"set directory",    P_BOOLREV, P_LOCAL,  &sDefault.bNo_set_dir,      NULL},
+  {"status",           P_BOOL,    P_LOCAL,  &sDefault.status,           NULL},
+  {"hide dot files",   P_BOOL,    P_LOCAL,  &sDefault.bHideDotFiles,    NULL},
+  {"guest only",       P_BOOL,    P_LOCAL,  &sDefault.bGuest_only,      NULL},
+  {"only guest",       P_BOOL,    P_LOCAL,  &sDefault.bGuest_only,      NULL},
+  {"guest ok",         P_BOOL,    P_LOCAL,  &sDefault.bGuest_ok,        NULL},
+  {"public",           P_BOOL,    P_LOCAL,  &sDefault.bGuest_ok,        NULL},
+  {"print ok",         P_BOOL,    P_LOCAL,  &sDefault.bPrint_ok,        NULL},
+  {"printable",        P_BOOL,    P_LOCAL,  &sDefault.bPrint_ok,        NULL},
+  {"postscript",       P_BOOL,    P_LOCAL,  &sDefault.bPostscript,      NULL},
+  {"map system",       P_BOOL,    P_LOCAL,  &sDefault.bMap_system,      NULL},
+  {"map hidden",       P_BOOL,    P_LOCAL,  &sDefault.bMap_hidden,      NULL},
+  {"map archive",      P_BOOL,    P_LOCAL,  &sDefault.bMap_archive,     NULL},
+  {"locking",          P_BOOL,    P_LOCAL,  &sDefault.bLocking,         NULL},
+  {"strict locking",   P_BOOL,    P_LOCAL,  &sDefault.bStrictLocking,   NULL},
+  {"share modes",      P_BOOL,    P_LOCAL,  &sDefault.bShareModes,      NULL},
+  {"only user",        P_BOOL,    P_LOCAL,  &sDefault.bOnlyUser,        NULL},
+  {"wide links",       P_BOOL,    P_LOCAL,  &sDefault.bWidelinks,       NULL},
+  {"sync always",      P_BOOL,    P_LOCAL,  &sDefault.bSyncAlways,      NULL},
+  {"mangled names",    P_BOOL,    P_LOCAL,  &sDefault.bMangledNames,    NULL},
+  {"print command",    P_STRING,  P_LOCAL,  &sDefault.szPrintcommand,   NULL},
+  {"lpq command",      P_STRING,  P_LOCAL,  &sDefault.szLpqcommand,     NULL},
+  {"lprm command",     P_STRING,  P_LOCAL,  &sDefault.szLprmcommand,    NULL},
+  {"lppause command",  P_STRING,  P_LOCAL,  &sDefault.szLppausecommand, NULL},
+  {"lpresume command", P_STRING,  P_LOCAL,  &sDefault.szLpresumecommand,NULL},
+  {"printer",          P_STRING,  P_LOCAL,  &sDefault.szPrintername,    NULL},
+  {"printer name",     P_STRING,  P_LOCAL,  &sDefault.szPrintername,    NULL},
+  {"hosts allow",      P_STRING,  P_LOCAL,  &sDefault.szHostsallow,     NULL},
+  {"allow hosts",      P_STRING,  P_LOCAL,  &sDefault.szHostsallow,     NULL},
+  {"hosts deny",       P_STRING,  P_LOCAL,  &sDefault.szHostsdeny,      NULL},
+  {"deny hosts",       P_STRING,  P_LOCAL,  &sDefault.szHostsdeny,      NULL},
+  {"dont descend",     P_STRING,  P_LOCAL,  &sDefault.szDontdescend,    NULL},
+  {"magic script",     P_STRING,  P_LOCAL,  &sDefault.szMagicScript,    NULL},
+  {"magic output",     P_STRING,  P_LOCAL,  &sDefault.szMagicOutput,    NULL},
+  {"mangled map",      P_STRING,  P_LOCAL,  &sDefault.szMangledMap,     NULL},
+
+  {NULL,               P_BOOL,    P_NONE,   NULL,                       NULL}
+};
+
+
+
+/***************************************************************************
+Initialise the global parameter structure.
+***************************************************************************/
+static void init_globals(void)
+{
+  static BOOL done_init = False;
+  pstring s;
+
+  if (!done_init)
+    {
+      int i;
+      bzero((void *)&Globals,sizeof(Globals));
+
+      for (i = 0; parm_table[i].label; i++) 
+       if (parm_table[i].type == P_STRING && 
+           parm_table[i].ptr)
+         string_init(parm_table[i].ptr,"");
+
+      string_set(&sDefault.szGuestaccount, GUEST_ACCOUNT);
+
+      done_init = True;
+    }
+
+
+  DEBUG(3,("Initialising global parameters\n"));
+
+#ifdef SMB_PASSWD_FILE
+  string_set(&Globals.szSMBPasswdFile, SMB_PASSWD_FILE);
+#endif
+  string_set(&Globals.szPasswdChat,"*old*password* %o\\n *new*password* %n\\n *new*password* %n\\n *changed*");
+  string_set(&Globals.szWorkGroup, WORKGROUP);
+#ifdef SMB_PASSWD
+  string_set(&Globals.szPasswdProgram, SMB_PASSWD);
+#else
+  string_set(&Globals.szPasswdProgram, "/bin/passwd");
+#endif
+  string_set(&Globals.szPrintcapname, PRINTCAP_NAME);
+  string_set(&Globals.szLockDir, LOCKDIR);
+  string_set(&Globals.szRootdir, "/");
+  sprintf(s,"Samba %s",VERSION);
+  string_set(&Globals.szServerString,s);
+  Globals.bLoadPrinters = True;
+  Globals.bUseRhosts = False;
+  Globals.max_packet = 65535;
+  Globals.mangled_stack = 50;
+  Globals.max_xmit = Globals.max_packet;
+  Globals.max_mux = 2;
+  Globals.lpqcachetime = 10;
+  Globals.pwordlevel = 0;
+  Globals.deadtime = 0;
+  Globals.max_log_size = 5000;
+  Globals.maxprotocol = PROTOCOL_NT1;
+  Globals.security = SEC_SHARE;
+  Globals.bEncryptPasswords = False;
+  Globals.printing = DEFAULT_PRINTING;
+  Globals.bReadRaw = True;
+  Globals.bWriteRaw = True;
+  Globals.bReadPrediction = False;
+  Globals.bReadbmpx = True;
+  Globals.bNullPasswords = False;
+  Globals.bStripDot = False;
+  Globals.syslog = 1;
+  Globals.bSyslogOnly = False;
+  Globals.os_level = 0;
+  Globals.max_ttl = 60*60*4; /* 2 hours default */
+  Globals.bPreferredMaster = True;
+  Globals.bDomainMaster = False;
+  Globals.bDomainLogons = False;
+  Globals.bBrowseList = True;
+
+#ifdef KANJI
+  coding_system = interpret_coding_system (KANJI, SJIS_CODE);
+#endif /* KANJI */
+
+}
+
+/***************************************************************************
+check if a string is initialised and if not then initialise it
+***************************************************************************/
+static void string_initial(char **s,char *v)
+{
+  if (!*s || !**s)
+    string_init(s,v);
+}
+
+
+/***************************************************************************
+Initialise the sDefault parameter structure.
+***************************************************************************/
+static void init_locals(void)
+{
+  /* choose defaults depending on the type of printing */
+  switch (Globals.printing)
+    {
+    case PRINT_BSD:
+    case PRINT_AIX:
+      string_initial(&sDefault.szLpqcommand,"lpq -P%p");
+      string_initial(&sDefault.szLprmcommand,"lprm -P%p %j");
+      string_initial(&sDefault.szPrintcommand,"lpr -r -P%p %s");
+      break;
+
+    case PRINT_SYSV:
+    case PRINT_HPUX:
+      string_initial(&sDefault.szLpqcommand,"lpstat -o%p");
+      string_initial(&sDefault.szLprmcommand,"cancel %p-%j");
+      string_initial(&sDefault.szPrintcommand,"lp -c -d%p %s; rm %s");
+#ifdef SVR4
+      string_initial(&sDefault.szLppausecommand,"lp -i %p-%j -H hold");
+      string_initial(&sDefault.szLpresumecommand,"lp -i %p-%j -H resume");
+#endif
+      break;
+
+    case PRINT_QNX:
+      string_initial(&sDefault.szLpqcommand,"lpq -P%p");
+      string_initial(&sDefault.szLprmcommand,"lprm -P%p %j");
+      string_initial(&sDefault.szPrintcommand,"lp -r -P%p %s");
+      break;
+
+      
+    }
+}
+
+
+/*******************************************************************
+a convenience rooutine to grab string parameters into a rotating
+static buffer, and run standard_sub_basic on them. The buffers
+can be written to by callers
+********************************************************************/
+char *lp_string(char *s)
+{
+  static pstring bufs[10];
+  static int next=0;
+  char *ret;
+  
+  ret = &bufs[next][0];
+  next = (next+1)%10;
+
+  if (!s) 
+    *ret = 0;
+  else
+    StrnCpy(ret,s,sizeof(pstring)-1);
+
+  standard_sub_basic(ret);
+  return(ret);
+}
+
+
+/*
+   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_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)&&pSERVICE(i)->val)?pSERVICE(i)->val : sDefault.val));}
+#define FN_LOCAL_BOOL(fn_name,val) \
+ BOOL fn_name(int i) {return(LP_SNUM_OK(i)? pSERVICE(i)->val : sDefault.val);}
+#define FN_LOCAL_CHAR(fn_name,val) \
+ char fn_name(int i) {return(LP_SNUM_OK(i)? pSERVICE(i)->val : sDefault.val);}
+#define FN_LOCAL_INTEGER(fn_name,val) \
+ int fn_name(int i) {return(LP_SNUM_OK(i)? pSERVICE(i)->val : sDefault.val);}
+
+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_serverstring,&Globals.szServerString)
+FN_GLOBAL_STRING(lp_printcapname,&Globals.szPrintcapname)
+FN_GLOBAL_STRING(lp_lockdir,&Globals.szLockDir)
+FN_GLOBAL_STRING(lp_rootdir,&Globals.szRootdir)
+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_workgroup,&Globals.szWorkGroup)
+FN_GLOBAL_STRING(lp_domain_controller,&Globals.szDomainController)
+FN_GLOBAL_STRING(lp_username_map,&Globals.szUsernameMap)
+FN_GLOBAL_STRING(lp_character_set,&Globals.szCharacterSet) 
+FN_GLOBAL_STRING(lp_logon_script,&Globals.szLogonScript) 
+
+FN_GLOBAL_BOOL(lp_domain_master,&Globals.bDomainMaster)
+FN_GLOBAL_BOOL(lp_domain_logons,&Globals.bDomainLogons)
+FN_GLOBAL_BOOL(lp_preferred_master,&Globals.bPreferredMaster)
+FN_GLOBAL_BOOL(lp_load_printers,&Globals.bLoadPrinters)
+FN_GLOBAL_BOOL(lp_use_rhosts,&Globals.bUseRhosts)
+FN_GLOBAL_BOOL(lp_getwdcache,&use_getwd_cache)
+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_writeraw,&Globals.bWriteRaw)
+FN_GLOBAL_BOOL(lp_null_passwords,&Globals.bNullPasswords)
+FN_GLOBAL_BOOL(lp_strip_dot,&Globals.bStripDot)
+FN_GLOBAL_BOOL(lp_encrypted_passwords,&Globals.bEncryptPasswords)
+FN_GLOBAL_BOOL(lp_syslog_only,&Globals.bSyslogOnly)
+FN_GLOBAL_BOOL(lp_browse_list,&Globals.bBrowseList)
+
+FN_GLOBAL_INTEGER(lp_os_level,&Globals.os_level)
+FN_GLOBAL_INTEGER(lp_max_ttl,&Globals.max_ttl)
+FN_GLOBAL_INTEGER(lp_max_log_size,&Globals.max_log_size)
+FN_GLOBAL_INTEGER(lp_mangledstack,&Globals.mangled_stack)
+FN_GLOBAL_INTEGER(lp_maxxmit,&Globals.max_xmit)
+FN_GLOBAL_INTEGER(lp_maxmux,&Globals.max_mux)
+FN_GLOBAL_INTEGER(lp_maxpacket,&Globals.max_packet)
+FN_GLOBAL_INTEGER(lp_keepalive,&keepalive)
+FN_GLOBAL_INTEGER(lp_passwordlevel,&Globals.pwordlevel)
+FN_GLOBAL_INTEGER(lp_deadtime,&Globals.deadtime)
+FN_GLOBAL_INTEGER(lp_maxprotocol,&Globals.maxprotocol)
+FN_GLOBAL_INTEGER(lp_security,&Globals.security)
+FN_GLOBAL_INTEGER(lp_printing,&Globals.printing)
+FN_GLOBAL_INTEGER(lp_maxdisksize,&Globals.maxdisksize)
+FN_GLOBAL_INTEGER(lp_lpqcachetime,&Globals.lpqcachetime)
+FN_GLOBAL_INTEGER(lp_syslog,&Globals.syslog)
+
+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_STRING(lp_pathname,szPath)
+FN_LOCAL_STRING(lp_dontdescend,szDontdescend)
+FN_LOCAL_STRING(lp_username,szUsername)
+FN_LOCAL_STRING(lp_guestaccount,szGuestaccount)
+FN_LOCAL_STRING(lp_invalid_users,szInvalidUsers)
+FN_LOCAL_STRING(lp_valid_users,szValidUsers)
+FN_LOCAL_STRING(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_printername,szPrintername)
+FN_LOCAL_STRING(lp_hostsallow,szHostsallow)
+FN_LOCAL_STRING(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_STRING(lp_readlist,readlist)
+FN_LOCAL_STRING(lp_writelist,writelist)
+FN_LOCAL_STRING(lp_volume,volume)
+FN_LOCAL_STRING(lp_mangled_map,szMangledMap)
+
+FN_LOCAL_BOOL(lp_alternate_permissions,bAlternatePerm)
+FN_LOCAL_BOOL(lp_revalidate,bRevalidate)
+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_status,status)
+FN_LOCAL_BOOL(lp_hide_dot_files,bHideDotFiles)
+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_postscript,bPostscript)
+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_share_modes,bShareModes)
+FN_LOCAL_BOOL(lp_onlyuser,bOnlyUser)
+FN_LOCAL_BOOL(lp_manglednames,bMangledNames)
+FN_LOCAL_BOOL(lp_widelinks,bWidelinks)
+FN_LOCAL_BOOL(lp_syncalways,bSyncAlways)
+FN_LOCAL_BOOL(lp_map_system,bMap_system)
+
+FN_LOCAL_INTEGER(lp_create_mode,iCreate_mode)
+FN_LOCAL_INTEGER(lp_max_connections,iMaxConnections)
+FN_LOCAL_INTEGER(lp_defaultcase,iDefaultCase)
+FN_LOCAL_INTEGER(lp_minprintspace,iMinPrintSpace)
+
+FN_LOCAL_CHAR(lp_magicchar,magic_char)
+
+
+
+/* local prototypes */
+static int    strwicmp( char *psz1, char *psz2 );
+static int    map_parameter( char *pszParmName);
+static BOOL   set_boolean( BOOL *pb, char *pszParmValue );
+static int    getservicebyname(char *pszServiceName, service *pserviceDest);
+static void   copy_service( service *pserviceDest, 
+                            service *pserviceSource,
+                            BOOL *pcopymapDest );
+static BOOL   service_ok(int iService);
+static BOOL   do_parameter(char *pszParmName, char *pszParmValue);
+static BOOL   do_section(char *pszSectionName);
+static void   dump_globals(void);
+static void   dump_a_service(service *pService);
+static void init_copymap(service *pservice);
+
+
+/***************************************************************************
+initialise a service to the defaults
+***************************************************************************/
+static void init_service(service *pservice)
+{
+  bzero((char *)pservice,sizeof(service));
+  copy_service(pservice,&sDefault,NULL);
+}
+
+
+/***************************************************************************
+free the dynamically allocated parts of a service struct
+***************************************************************************/
+static void free_service(service *pservice)
+{
+  int i;
+  if (!pservice)
+     return;
+
+  for (i=0;parm_table[i].label;i++)
+    if (parm_table[i].type == P_STRING && parm_table[i].class == P_LOCAL)
+      string_free((char **)(((char *)pservice) + PTR_DIFF(parm_table[i].ptr,&sDefault)));
+}
+
+/***************************************************************************
+add a new service to the services array initialising it with the given 
+service
+***************************************************************************/
+static int add_a_service(service *pservice, char *name)
+{
+  int i;
+  service tservice;
+  int num_to_alloc = iNumServices+1;
+
+  tservice = *pservice;
+
+  /* it might already exist */
+  if (name) 
+    {
+      i = getservicebyname(name,NULL);
+      if (i >= 0)
+       return(i);
+    }
+
+  /* find an invalid one */
+  for (i=0;i<iNumServices;i++)
+    if (!pSERVICE(i)->valid)
+      break;
+
+  /* if not, then create one */
+  if (i == iNumServices)
+    {
+      ServicePtrs = (service **)Realloc(ServicePtrs,sizeof(service *)*num_to_alloc);
+      if (ServicePtrs)
+       pSERVICE(iNumServices) = (service *)malloc(sizeof(service));
+
+      if (!ServicePtrs || !pSERVICE(iNumServices))
+       return(-1);
+
+      iNumServices++;
+    }
+  else
+    free_service(pSERVICE(i));
+
+  pSERVICE(i)->valid = True;
+
+  init_service(pSERVICE(i));
+  copy_service(pSERVICE(i),&tservice,NULL);
+  if (name)
+    string_set(&iSERVICE(i).szService,name);  
+
+  return(i);
+}
+
+/***************************************************************************
+add a new home service, with the specified home directory, defaults coming 
+from service ifrom
+***************************************************************************/
+BOOL lp_add_home(char *pszHomename, int iDefaultService, char *pszHomedir)
+{
+  int i = add_a_service(pSERVICE(iDefaultService),pszHomename);
+
+  if (i < 0)
+    return(False);
+
+  if (!(*(iSERVICE(i).szPath)) || strequal(iSERVICE(i).szPath,lp_pathname(-1)))
+    string_set(&iSERVICE(i).szPath,pszHomedir);
+  if (!(*(iSERVICE(i).comment)))
+    {
+      pstring comment;
+      sprintf(comment,"Home directory of %s",pszHomename);
+      string_set(&iSERVICE(i).comment,comment);
+    }
+  iSERVICE(i).bAvailable = sDefault.bAvailable;
+  iSERVICE(i).bBrowseable = sDefault.bBrowseable;
+
+  DEBUG(3,("adding home directory %s at %s\n", pszHomename, pszHomedir));
+
+  return(True);
+}
+
+/***************************************************************************
+add a new service, based on an old one
+***************************************************************************/
+int lp_add_service(char *pszService, int iDefaultService)
+{
+  return(add_a_service(pSERVICE(iDefaultService),pszService));
+}
+
+
+/***************************************************************************
+add the IPC service
+***************************************************************************/
+static BOOL lp_add_ipc(void)
+{
+  pstring comment;
+  int i = add_a_service(&sDefault,"IPC$");
+
+  if (i < 0)
+    return(False);
+
+  sprintf(comment,"IPC Service (%s)",lp_serverstring());
+
+  string_set(&iSERVICE(i).szPath,"/tmp");
+  string_set(&iSERVICE(i).szUsername,"");
+  string_set(&iSERVICE(i).comment,comment);
+  iSERVICE(i).status = False;
+  iSERVICE(i).iMaxConnections = 0;
+  iSERVICE(i).bAvailable = True;
+  iSERVICE(i).bRead_only = True;
+  iSERVICE(i).bGuest_only = False;
+  iSERVICE(i).bGuest_ok = True;
+  iSERVICE(i).bPrint_ok = False;
+  iSERVICE(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(char *pszPrintername, int iDefaultService)
+{
+  char *comment = "From Printcap";
+  int i = add_a_service(pSERVICE(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(&iSERVICE(i).szPrintername,pszPrintername);
+  string_set(&iSERVICE(i).comment,comment);
+  iSERVICE(i).bBrowseable = sDefault.bBrowseable;
+  
+  DEBUG(3,("adding printer service %s\n",pszPrintername));
+  
+  return(True);
+}
+
+
+/***************************************************************************
+Do a case-insensitive, whitespace-ignoring string compare.
+***************************************************************************/
+static int strwicmp(char *psz1, 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(*psz1))
+         psz1++;
+      while (isspace(*psz2))
+         psz2++;
+      if (toupper(*psz1) != toupper(*psz2) || *psz1 == '\0' || *psz2 == '\0')
+         break;
+      psz1++;
+      psz2++;
+   }
+   return (*psz1 - *psz2);
+}
+
+/***************************************************************************
+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(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);
+
+   DEBUG(0,( "Unknown parameter encountered: \"%s\"\n", pszParmName));
+   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, 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,( "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(char *pszServiceName, service *pserviceDest)
+{
+   int iService;
+
+   for (iService = iNumServices - 1; iService >= 0; iService--)
+      if (VALID(iService) &&
+         strwicmp(iSERVICE(iService).szService, pszServiceName) == 0) 
+      {
+         if (pserviceDest != NULL)
+          copy_service(pserviceDest, pSERVICE(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);
+
+  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_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;
+         default:
+           break;
+         }
+      }
+
+  if (bcopyall)
+    {
+      init_copymap(pserviceDest);
+      if (pserviceSource->copymap)
+       memcpy((void *)pserviceDest->copymap,
+              (void *)pserviceSource->copymap,sizeof(BOOL)*NUMPARAMETERS);
+    }
+}
+
+/***************************************************************************
+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 (iSERVICE(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(iSERVICE(iService).szService,PRINTERS_NAME) == 0)
+      if (!iSERVICE(iService).bPrint_ok)
+      {
+         DEBUG(0,( "WARNING: [%s] service MUST be printable!\n",
+               iSERVICE(iService).szService));
+        iSERVICE(iService).bPrint_ok = True;
+      }
+
+   if (iSERVICE(iService).szPath[0] == '\0' &&
+       strwicmp(iSERVICE(iService).szService,HOMES_NAME) != 0)
+   {
+      DEBUG(0,("No path in service %s - using /tmp\n",iSERVICE(iService).szService));
+      string_set(&iSERVICE(iService).szPath,"/tmp");      
+   }
+
+   /* If a service is flagged unavailable, log the fact at level 0. */
+   if (!iSERVICE(iService).bAvailable) 
+      DEBUG(1,( "NOTE: Service %s is flagged unavailable.\n",
+            iSERVICE(iService).szService));
+
+   return (bRetval);
+}
+
+static struct file_lists {
+  struct file_lists *next;
+  char *name;
+  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(char *fname)
+{
+  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) {
+      free(f);
+      return;
+    }
+    file_lists = f;
+  }
+
+  {
+    pstring n2;
+    strcpy(n2,fname);
+    standard_sub_basic(n2);
+    f->modtime = file_modtime(n2);
+  }
+
+}
+
+/*******************************************************************
+check if a config file has changed date
+********************************************************************/
+BOOL lp_file_list_changed(void)
+{
+  struct file_lists *f = file_lists;
+  while (f) {
+    pstring n2;
+    strcpy(n2,f->name);
+    standard_sub_basic(n2);
+    if (f->modtime != file_modtime(n2)) return(True);
+    f = f->next;   
+  }
+  return(False);
+}
+
+#ifdef KANJI
+/***************************************************************************
+  handle the interpretation of the coding system parameter
+  *************************************************************************/
+static BOOL handle_coding_system(char *pszParmValue,int *val)
+{
+  *val = interpret_coding_system(pszParmValue,*val);
+  return(True);
+}
+#endif /* KANJI */
+
+/***************************************************************************
+handle the interpretation of the character set system parameter
+***************************************************************************/
+static BOOL handle_character_set(char *pszParmValue,int *val)
+{
+  string_set(&Globals.szCharacterSet,pszParmValue);
+  *val = interpret_character_set(pszParmValue,*val);
+  return(True);
+}
+
+
+/***************************************************************************
+handle the interpretation of the protocol parameter
+***************************************************************************/
+static BOOL handle_protocol(char *pszParmValue,int *val)
+{
+  *val = interpret_protocol(pszParmValue,*val);
+  return(True);
+}
+
+/***************************************************************************
+handle the interpretation of the security parameter
+***************************************************************************/
+static BOOL handle_security(char *pszParmValue,int *val)
+{
+  *val = interpret_security(pszParmValue,*val);
+  return(True);
+}
+
+/***************************************************************************
+handle the interpretation of the default case
+***************************************************************************/
+static BOOL handle_case(char *pszParmValue,int *val)
+{
+  if (strequal(pszParmValue,"LOWER"))
+    *val = CASE_LOWER;
+  else if (strequal(pszParmValue,"UPPER"))
+    *val = CASE_UPPER;
+  return(True);
+}
+
+/***************************************************************************
+handle the interpretation of the printing system
+***************************************************************************/
+static BOOL handle_printing(char *pszParmValue,int *val)
+{
+  if (strequal(pszParmValue,"sysv"))
+    *val = PRINT_SYSV;
+  else if (strequal(pszParmValue,"aix"))
+    *val = PRINT_AIX;
+  else if (strequal(pszParmValue,"hpux"))
+    *val = PRINT_HPUX;
+  else if (strequal(pszParmValue,"bsd"))
+    *val = PRINT_BSD;
+  else if (strequal(pszParmValue,"qnx"))
+    *val = PRINT_QNX;
+  return(True);
+}
+
+/***************************************************************************
+handle the valid chars lines
+***************************************************************************/
+static BOOL handle_valid_chars(char *pszParmValue,char **ptr)
+{ 
+  string_set(ptr,pszParmValue);
+
+  add_char_string(pszParmValue);
+  return(True);
+}
+
+
+/***************************************************************************
+handle the include operation
+***************************************************************************/
+static BOOL handle_include(char *pszParmValue,char **ptr)
+{ 
+  pstring fname;
+  strcpy(fname,pszParmValue);
+
+  add_to_file_list(fname);
+
+  standard_sub_basic(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(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(pSERVICE(iServiceIndex), 
+                       &serviceTemp,
+                       iSERVICE(iServiceIndex).copymap);
+          bRetval = True;
+        }
+     }
+   else
+     {
+       DEBUG(0,( "Unable to copy service - source not found: %s\n",
+               pszParmValue));
+       bRetval = False;
+     }
+
+   free_service(&serviceTemp);
+   return (bRetval);
+}
+
+
+/***************************************************************************
+initialise a copymap
+***************************************************************************/
+static void init_copymap(service *pservice)
+{
+  int i;
+  if (pservice->copymap) free(pservice->copymap);
+  pservice->copymap = (BOOL *)malloc(sizeof(BOOL)*NUMPARAMETERS);
+  if (!pservice->copymap)
+    DEBUG(0,("Couldn't allocate copymap!! (size %d)\n",NUMPARAMETERS));
+
+  for (i=0;i<NUMPARAMETERS;i++)
+    pservice->copymap[i] = True;
+}
+
+
+/***************************************************************************
+Process a parameter.
+***************************************************************************/
+static BOOL do_parameter(char *pszParmName, char *pszParmValue)
+{
+   int parmnum;
+   void *parm_ptr=NULL; /* where we are going to store the result */
+   void *def_ptr=NULL;
+
+   if (!bInGlobalSection && bGlobalOnly) return(True);
+
+   DEBUG(3,("doing parameter %s = %s\n",pszParmName,pszParmValue));
+   
+   parmnum = map_parameter(pszParmName);
+
+   if (parmnum < 0)
+     {
+       DEBUG(0,( "Ignoring unknown parameter \"%s\"\n", pszParmName));
+       return(True);
+     }
+
+   def_ptr = parm_table[parmnum].ptr;
+
+   /* we might point at a service, the default service or a global */
+   if (bInGlobalSection)
+     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 *)pSERVICE(iServiceIndex)) + PTR_DIFF(def_ptr,&sDefault);
+     }
+
+   if (!bInGlobalSection)
+     {
+       int i;
+       if (!iSERVICE(iServiceIndex).copymap)
+        init_copymap(pSERVICE(iServiceIndex));
+       
+       /* 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)
+          iSERVICE(iServiceIndex).copymap[i] = False;
+     }
+
+   /* if it is a special case then go ahead */
+   if (parm_table[parmnum].special)
+     {
+       parm_table[parmnum].special(pszParmValue,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_STRING:
+       string_set(parm_ptr,pszParmValue);
+       break;
+
+     case P_GSTRING:
+       strcpy((char *)parm_ptr,pszParmValue);
+       break;
+     }
+
+   return(True);
+}
+
+/***************************************************************************
+print a parameter of the specified type
+***************************************************************************/
+static void print_parameter(parm_type type,void *ptr)
+{
+  switch (type)
+    {
+    case P_BOOL:
+      printf("%s",BOOLSTR(*(BOOL *)ptr));
+      break;
+      
+    case P_BOOLREV:
+      printf("%s",BOOLSTR(! *(BOOL *)ptr));
+      break;
+      
+    case P_INTEGER:
+      printf("%d",*(int *)ptr);
+      break;
+      
+    case P_CHAR:
+      printf("%c",*(char *)ptr);
+      break;
+      
+    case P_OCTAL:
+      printf("0%o",*(int *)ptr);
+      break;
+      
+    case P_GSTRING:
+      if ((char *)ptr)
+       printf("%s",(char *)ptr);
+      break;
+
+    case P_STRING:
+      if (*(char **)ptr)
+       printf("%s",*(char **)ptr);
+      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_OCTAL:
+      return(*((int *)ptr1) == *((int *)ptr2));
+      
+    case P_CHAR:
+      return(*((char *)ptr1) == *((char *)ptr2));
+
+    case P_GSTRING:
+      {
+       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_STRING:
+      {
+       char *p1 = *(char **)ptr1, *p2 = *(char **)ptr2;
+       if (p1 && !*p1) p1 = NULL;
+       if (p2 && !*p2) p2 = NULL;
+       return(p1==p2 || strequal(p1,p2));
+      }
+    }
+  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(char *pszSectionName)
+{
+   BOOL bRetval;
+   BOOL isglobal = ((strwicmp(pszSectionName, GLOBAL_NAME) == 0) || 
+                   (strwicmp(pszSectionName, GLOBAL_NAME2) == 0));
+   bRetval = False;
+
+   /* if we were in a global section then do the local inits */
+   if (bInGlobalSection && !isglobal)
+     init_locals();
+
+   /* 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);
+}
+
+/***************************************************************************
+Display the contents of the global structure.
+***************************************************************************/
+static void dump_globals(void)
+{
+  int i;
+  printf("Global parameters:\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)))
+      {
+       printf("\t%s: ",parm_table[i].label);
+       print_parameter(parm_table[i].type,parm_table[i].ptr);
+       printf("\n");
+      }
+}
+
+/***************************************************************************
+Display the contents of a single services record.
+***************************************************************************/
+static void dump_a_service(service *pService)
+{
+  int i;
+  if (pService == &sDefault)
+    printf("\nDefault service parameters:\n");
+  else
+    printf("\nService parameters [%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 || !equal_parameter(parm_table[i].type,
+                                                     ((char *)pService) + pdiff,
+                                                     ((char *)&sDefault) + pdiff))
+         {
+           printf("\t%s: ",parm_table[i].label);
+           print_parameter(parm_table[i].type,
+                           ((char *)pService) + pdiff);
+           printf("\n");
+         }
+      }
+}
+
+#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) && iSERVICE(iService).bAvailable);
+}
+
+
+/***************************************************************************
+auto-load some homes and printer services
+***************************************************************************/
+static void lp_add_auto_services(char *str)
+{
+  char *s;
+  char *p;
+  int homes = lp_servicenumber(HOMES_NAME);
+  int printers = lp_servicenumber(PRINTERS_NAME);
+
+  if (!str)
+    return;
+
+  s = strdup(str);
+  if (!s) return;
+
+  for (p=strtok(s,LIST_SEP);p;p=strtok(NULL,LIST_SEP))
+    {
+      char *home = get_home_dir(p);
+
+      if (lp_servicenumber(p) >= 0) continue;
+
+      if (home && homes >= 0)
+       {
+         lp_add_home(p,homes,home);
+         continue;
+       }
+
+      if (printers >= 0 && pcap_printername_ok(p,NULL))
+       lp_add_printer(p,printers);
+    }
+  free(s);
+}
+
+/***************************************************************************
+auto-load one printer
+***************************************************************************/
+static 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(&iSERVICE(i).comment,comment);
+    }      
+}
+
+
+/***************************************************************************
+auto-load printer services
+***************************************************************************/
+static void lp_add_all_printers(void)
+{
+  int printers = lp_servicenumber(PRINTERS_NAME);
+
+  if (printers < 0) return;
+
+  pcap_printer_fn(lp_add_one_printer);
+}
+
+/***************************************************************************
+have we loaded a services file yet?
+***************************************************************************/
+BOOL lp_loaded(void)
+{
+  return(bLoaded);
+}
+
+/***************************************************************************
+unload unused services
+***************************************************************************/
+void lp_killunused(BOOL (*snumused)(int ))
+{
+  int i;
+  for (i=0;i<iNumServices;i++)
+    if (VALID(i) && !snumused(i))
+      {
+       iSERVICE(i).valid = False;
+       free_service(pSERVICE(i));
+      }
+}
+
+/***************************************************************************
+Load the services array from the services file. Return True on success, 
+False on failure.
+***************************************************************************/
+BOOL lp_load(char *pszFname,BOOL global_only)
+{
+  pstring n2;
+  BOOL bRetval;
+  
+  add_to_file_list(pszFname);
+
+  bRetval = False;
+
+  bInGlobalSection = True;
+  bGlobalOnly = global_only;
+  
+  init_globals();
+  
+  strcpy(n2,pszFname);
+  standard_sub_basic(n2);
+
+  /* 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(3,("pm_process() returned %s\n", BOOLSTR(bRetval)));
+  if (bRetval)
+    if (iServiceIndex >= 0)
+      bRetval = service_ok(iServiceIndex);        
+
+  lp_add_auto_services(lp_auto_services());
+  if (lp_load_printers())
+    lp_add_all_printers();
+
+  lp_add_ipc();
+
+  bLoaded = True;
+
+  return (bRetval);
+}
+
+
+/***************************************************************************
+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(void)
+{
+   int iService;
+
+   dump_globals();
+   
+   dump_a_service(&sDefault);
+
+   for (iService = 0; iService < iNumServices; iService++)
+   {
+     if (VALID(iService))
+       {
+        if (iSERVICE(iService).szService[0] == '\0')
+          break;
+        dump_a_service(pSERVICE(iService));
+       }
+   }
+}
+
+/***************************************************************************
+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(char *pszServiceName)
+{
+   int iService;
+
+   for (iService = iNumServices - 1; iService >= 0; iService--)
+      if (VALID(iService) &&
+         strwicmp(iSERVICE(iService).szService, pszServiceName) == 0) 
+         break;
+
+   if (iService < 0)
+     DEBUG(7,("lp_servicenumber: couldn't find %s\n",pszServiceName));
+   
+   return (iService);
+}
+
+
+
+
+/*******************************************************************
+  get a workgroup - but map to standalone if '*'
+  ******************************************************************/
+char *my_workgroup(void)
+{
+  char *res = lp_workgroup();
+  if (*res == '*') return("STANDALONE");
+  return(res);
+}
+
+/*******************************************************************
+  a useful volume label function
+  ******************************************************************/
+char *volume_label(int snum)
+{
+  char *ret = lp_volume(snum);
+  if (!*ret) return(lp_servicename(snum));
+  return(ret);
+}
diff --git a/source/param/params.c b/source/param/params.c
new file mode 100644 (file)
index 0000000..b9d6138
--- /dev/null
@@ -0,0 +1,335 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   Parameter loading utlities
+   Copyright (C) Karl Auer 1993,1994
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/**************************************************************************
+PARAMS.C
+
+Copyright (C) 1990, 1991, 1992, 1993, 1994 Karl Auer
+
+This module provides for streamlines retrieval of information from a
+Windows-like parameter files. There is a function which will search for
+all sections in the file and call a specified function with each. There is
+a similar function which will call a specified function for all parameters 
+in a section. The idea is that you pass the addresses of suitable functions
+to a single function in this module which will then enumerate all sections, 
+and within each section all parameters, to your program. 
+
+Parameter files contain text lines (newline delimited) which consist of
+either a section name in square brackets or a parameter name, delimited
+from the parameter value by an equals sign. Blank lines or lines where the
+first non-whitespace character is a colon are ignored. All whitespace in
+section names and parameter names is compressed to single spaces. Leading 
+and trailing whitespace on parameter names and parameter values is stripped.
+
+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. Parameter names may not start 
+with a square bracket, an equals sign or a semicolon, for obvious reasons. 
+
+A sample parameter file might look like this:
+
+[things]
+this=1
+that=2
+[other things]
+the other = 3
+
+**************************************************************************/
+
+#include "includes.h"
+
+#include "smb.h"
+#include "params.h"
+
+/* local variable pointing to passed filename */
+static char *pszParmFile = NULL;
+extern int DEBUGLEVEL;
+
+/* local prototypes */
+static BOOL enumerate_parameters(FILE *infile, PM_PARMFUNC pfunc);
+static BOOL enumerate_sections(FILE *infile, 
+                              PM_SECFUNC sfunc, PM_PARMFUNC pfunc);
+
+/* prototypes for local toolbox functions */
+static void trimleft(char *psz);
+static void trimright(char *psz);
+static void collapse_spaces(char *psz);
+static int  firstnonwhite(char *psz);
+
+/**************************************************************************
+Identifies all parameters in the current section, calls the parameter
+function for each. Ignores comment lines, stops and backs up in file when
+a section is encountered. Returns True on success, False on error.
+**************************************************************************/
+static BOOL enumerate_parameters(FILE *fileIn, PM_PARMFUNC pfunc)
+{
+   pstring szBuf;
+   char *pszTemp;
+   BOOL bRetval;
+   long lFileOffset;
+   int  cTemp;
+   BOOL bParmFound;
+
+   bRetval = False;
+   bParmFound = False;
+   while (True)
+   {
+      /* first remember where we are */
+      if ((lFileOffset = ftell(fileIn)) >= 0L)
+      {
+        /* then get and check a line */
+        if (fgets_slash(szBuf, sizeof(szBuf)-1, fileIn) == NULL)
+        {
+           /* stop - return OK unless file error */
+           bRetval = !ferror(fileIn);
+            if (!bRetval)
+             DEBUG(0,( "Read error on configuration file (enumerating parameters)!\n"));
+           break;   
+        }
+        else
+           /* if first non-white is a '[', stop (new section) */
+           if ((cTemp = firstnonwhite(szBuf)) == '[')
+           {
+              /* restore position to start of new section */
+              if (fseek(fileIn, lFileOffset, SEEK_SET) < 0L)
+              {
+                  DEBUG(0,( "Seek error on configuration file!\n"));
+                  break;
+              }
+
+              /* return success */
+              bRetval = True;
+              break;
+           }
+           else
+              /* if it's a semicolon or line is blank, ignore the line */
+              if (!cTemp || strchr(";#",cTemp))
+              {
+                 continue;
+              }
+              else
+                 /* if no equals sign and line contains non-whitespace */
+                 /* then line is badly formed */
+                 if ((pszTemp = strchr(szBuf, '=')) == NULL)
+                 {
+                    DEBUG(0,( "Ignoring badly formed line: %s", szBuf));
+                 }
+                 else
+                 {
+                     /* Note that we have found a parameter */
+                     bParmFound = True;
+                    /* cut line at the equals sign */
+                    *pszTemp++ = '\0';
+                    /* trim leading and trailing space from both halves */
+                    trimright(szBuf);
+                    trimleft(szBuf);
+                    trimright(pszTemp);
+                    trimleft(pszTemp);
+                    /* process the parameter iff passed pointer not NULL */
+                    if (pfunc != NULL)
+                        if (!pfunc(szBuf, pszTemp))
+                          break;
+                 }
+      }
+   }
+   return (bRetval);
+}
+
+
+/***********************************************************************
+Close up s by n chars, at offset start.
+***********************************************************************/
+static void closestr(char *s, int start, int n)
+{
+   char *src;
+   char *dest;
+   int  len;
+
+   if (n > 0)
+      if ((src = dest = s) != NULL)
+      {
+         len = strlen(s);
+         if (start >= 0 && start < len - n)
+         {
+            src += start + n;
+            dest += start;
+  
+            while (*src)
+               *dest++ = *src++;
+            *dest = '\0';
+         }
+      }
+}
+
+/**************************************************************************
+Identifies all sections in the parameter file, calls passed section_func()
+for each, passing the section name, then calls enumerate_parameters(). 
+Returns True on success, False on failure. Note that the section and 
+parameter names will have all internal whitespace areas collapsed to a 
+single space for processing.
+**************************************************************************/
+static BOOL enumerate_sections(FILE *fileIn, 
+                              PM_SECFUNC sfunc, PM_PARMFUNC pfunc)
+{
+   pstring szBuf;
+   BOOL bRetval;
+   BOOL bSectionFound;
+
+   /* this makes sure we get include lines right */
+   enumerate_parameters(fileIn, pfunc);
+
+   bRetval = False;
+   bSectionFound = False;
+   while (True)
+   {
+      if (fgets_slash(szBuf, sizeof(szBuf)-1, fileIn) == NULL)
+      {
+        /* stop - return OK unless file error */
+        bRetval = !ferror(fileIn);
+         if (!bRetval)
+          DEBUG(0,( "Read error on configuration file (enumerating sections)!\n"));
+        break;   
+      }
+      else
+      {
+        trimleft(szBuf);
+        trimright(szBuf);
+        if (szBuf[0] == '[')
+        {
+           closestr(szBuf, 0, 1);
+           if (strlen(szBuf) > 1)
+              if (szBuf[strlen(szBuf) - 1] == ']')
+              {  
+                 /* found a section - note the fact */
+                  bSectionFound = True;
+                 /* remove trailing metabracket */
+                 szBuf[strlen(szBuf) - 1] = '\0';
+                 /* remove leading and trailing whitespace from name */
+                 trimleft(szBuf);
+                 trimright(szBuf);
+                 /* reduce all internal whitespace to one space */
+                 collapse_spaces(szBuf);
+                 /* process it - stop if the processing fails */
+                 if (sfunc != NULL)
+                     if (!sfunc(szBuf))
+                       break;
+                 if (!enumerate_parameters(fileIn, pfunc))
+                     break;
+              }
+        }
+      }
+   }
+
+   return (bRetval);
+}
+
+/**************************************************************************
+Process the passed parameter file.
+
+Returns True if successful, else False.
+**************************************************************************/
+BOOL pm_process(char *pszFileName, PM_SECFUNC sfunc, PM_PARMFUNC pfunc)
+{
+   FILE *fileIn;
+   BOOL bRetval;
+
+   bRetval = False;
+
+   /* record the filename for use in error messages one day... */
+   pszParmFile = pszFileName;
+
+   if (pszParmFile == NULL || strlen(pszParmFile) < 1)
+      DEBUG(0,( "No configuration filename specified!\n"));
+   else
+      if ((fileIn = fopen(pszParmFile, "r")) == NULL)
+         DEBUG(0,( "Unable to open configuration file \"%s\"!\n", pszParmFile));
+      else
+      {
+         DEBUG(2,( "Processing configuration file \"%s\"\n", pszParmFile));
+        bRetval = enumerate_sections(fileIn, sfunc, pfunc);
+        fclose(fileIn);
+      }
+
+   if (!bRetval)
+     DEBUG(0,("pm_process retuned false\n"));
+   return (bRetval);
+}
+
+
+/**************************************************************************
+Strip all leading whitespace from a string.
+**************************************************************************/
+static void trimleft(char *psz)
+{
+   char *pszDest;
+
+   pszDest = psz;
+   if (psz != NULL)
+   {
+      while (*psz != '\0' && isspace(*psz))
+        psz++;
+      while (*psz != '\0')
+        *pszDest++ = *psz++;
+      *pszDest = '\0';
+   }
+}
+
+/**************************************************************************
+Strip all trailing whitespace from a string.
+**************************************************************************/
+static void trimright(char *psz)
+{
+   char *pszTemp;
+
+   if (psz != NULL && psz[0] != '\0')
+   {
+      pszTemp = psz + strlen(psz) - 1;
+      while (isspace(*pszTemp))
+        *pszTemp-- = '\0';
+   }
+}
+
+/***********************************************************************
+Collapse each whitespace area in a string to a single space.
+***********************************************************************/
+static void collapse_spaces(char *psz)
+{
+   while (*psz)
+      if (isspace(*psz))
+      {
+        *psz++ = ' ';
+        trimleft(psz);
+      }
+      else
+        psz++;
+}
+
+/**************************************************************************
+Return the value of the first non-white character in the specified string.
+The terminating NUL counts as non-white for the purposes of this function.
+Note - no check for a NULL string! What would we return?
+**************************************************************************/
+static int firstnonwhite(char *psz)
+{
+   while (isspace(*psz) && (*psz != '\0'))
+      psz++;
+   return (*psz);
+}
diff --git a/source/passdb/smbpass.c b/source/passdb/smbpass.c
new file mode 100644 (file)
index 0000000..2dec15f
--- /dev/null
@@ -0,0 +1,304 @@
+#ifdef SMB_PASSWD
+/*
+ * Unix SMB/Netbios implementation. Version 1.9. SMB parameters and setup
+ * Copyright (C) Andrew Tridgell 1992-1995 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"
+#include "loadparm.h"
+
+extern int      DEBUGLEVEL;
+
+int             gotalarm;
+
+void 
+gotalarm_sig()
+{
+       gotalarm = 1;
+}
+
+int 
+do_pw_lock(int fd, int waitsecs, int type)
+{
+       struct flock    lock;
+       int             ret;
+
+       gotalarm = 0;
+       signal(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(5);
+       ret = fcntl(fd, F_SETLKW, &lock);
+       alarm(0);
+       signal(SIGALRM, SIGNAL_CAST SIG_DFL);
+
+       if (gotalarm) {
+               DEBUG(0, ("do_pw_lock: failed to %s SMB passwd file.\n",
+                         type == F_UNLCK ? "unlock" : "lock"));
+               return -1;
+       }
+       return ret;
+}
+
+int 
+pw_file_lock(char *name, int type, int secs)
+{
+       int             fd = open(name, O_RDWR | O_CREAT, 0666);
+       if (fd < 0)
+               return (-1);
+       if (do_pw_lock(fd, secs, type)) {
+               close(fd);
+               return -1;
+       }
+       return fd;
+}
+
+int 
+pw_file_unlock(int fd)
+{
+       do_pw_lock(fd, 5, F_UNLCK);
+       return close(fd);
+}
+
+/*
+ * Routine to get the next 32 hex characters and turn them
+ * into a 16 byte array.
+ */
+
+static int gethexpwd(char *p, char *pwd)
+{
+       int i;
+       unsigned char   lonybble, hinybble;
+       char           *hexchars = "0123456789ABCDEF";
+       char           *p1, *p2;
+
+       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);
+}
+
+/*
+ * Routine to search the smbpasswd file for an entry matching the username.
+ */
+struct smb_passwd *
+get_smbpwnam(char *name)
+{
+       /* Static buffers we will return. */
+       static struct smb_passwd pw_buf;
+       static pstring  user_name;
+       static unsigned char smbpwd[16];
+       static unsigned char smbntpwd[16];
+       char            linebuf[256];
+       char            readbuf[16 * 1024];
+       unsigned char   c;
+       unsigned char  *p;
+       long            uidval;
+       long            linebuf_len;
+       FILE           *fp;
+       int             lockfd;
+       char           *pfile = lp_smb_passwd_file();
+
+       if (!*pfile) {
+               DEBUG(0, ("No SMB password file set\n"));
+               return (NULL);
+       }
+       DEBUG(10, ("get_smbpwnam: opening file %s\n", pfile));
+
+       fp = fopen(pfile, "r");
+
+       if (fp == NULL) {
+               DEBUG(0, ("get_smbpwnam: unable to open file %s\n", pfile));
+               return NULL;
+       }
+       /* Set a 16k buffer to do more efficient reads */
+       setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
+
+       if ((lockfd = pw_file_lock(pfile, F_RDLCK, 5)) < 0) {
+               DEBUG(0, ("get_smbpwnam: unable to lock file %s\n", pfile));
+               fclose(fp);
+               return NULL;
+       }
+       /* make sure it is only rw by the owner */
+       chmod(pfile, 0600);
+
+       /* We have a read lock on the file. */
+       /*
+        * 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)) {
+                       fclose(fp);
+                       pw_file_unlock(lockfd);
+                       return NULL;
+               }
+               /*
+                * 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, ("get_smbpwnam: got line |%s|\n", linebuf));
+#endif
+               if ((linebuf[0] == 0) && feof(fp)) {
+                       DEBUG(4, ("get_smbpwnam: 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]:....ignored....
+                *
+                * if Windows NT compatible passwords are also present.
+                */
+
+               if (linebuf[0] == '#' || linebuf[0] == '\0') {
+                       DEBUG(6, ("get_smbpwnam: skipping comment or blank line\n"));
+                       continue;
+               }
+               p = (unsigned char *) strchr(linebuf, ':');
+               if (p == NULL) {
+                       DEBUG(0, ("get_smbpwnam: 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....
+                */
+               strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
+               user_name[PTR_DIFF(p, linebuf)] = '\0';
+               if (!strequal(user_name, name))
+                       continue;
+
+               /* User name matches - get uid and password */
+               p++;            /* Go past ':' */
+               if (!isdigit(*p)) {
+                       DEBUG(0, ("get_smbpwnam: malformed password entry (uid not number)\n"));
+                       fclose(fp);
+                       pw_file_unlock(lockfd);
+                       return NULL;
+               }
+               uidval = atoi((char *) p);
+               while (*p && isdigit(*p))
+                       p++;
+               if (*p != ':') {
+                       DEBUG(0, ("get_smbpwnam: malformed password entry (no : after uid)\n"));
+                       fclose(fp);
+                       pw_file_unlock(lockfd);
+                       return NULL;
+               }
+               /*
+                * 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++;
+               if (*p == '*' || *p == 'X') {
+                       /* Password deliberately invalid - end here. */
+                       DEBUG(10, ("get_smbpwnam: entry invalidated for user %s\n", user_name));
+                       fclose(fp);
+                       pw_file_unlock(lockfd);
+                       return NULL;
+               }
+               if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
+                       DEBUG(0, ("get_smbpwnam: malformed password entry (passwd too short)\n"));
+                       fclose(fp);
+                       pw_file_unlock(lockfd);
+                       return (False);
+               }
+               if (p[32] != ':') {
+                       DEBUG(0, ("get_smbpwnam: malformed password entry (no terminating :)\n"));
+                       fclose(fp);
+                       pw_file_unlock(lockfd);
+                       return NULL;
+               }
+               if (!strncasecmp((char *) p, "NO PASSWORD", 11)) {
+                       pw_buf.smb_passwd = NULL;
+               } else {
+                       if(!gethexpwd(p,smbpwd)) {
+                               DEBUG(0, ("Malformed Lanman password entry (non hex chars)\n"));
+                               fclose(fp);
+                               pw_file_unlock(lockfd);
+                               return NULL;
+                       }
+                       pw_buf.smb_passwd = smbpwd;
+               }
+               pw_buf.smb_name = user_name;
+               pw_buf.smb_userid = uidval;
+               pw_buf.smb_nt_passwd = NULL;
+
+               /* 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)) && (p[32] == ':')) {
+                       if (*p != '*' && *p != 'X') {
+                               if(gethexpwd(p,smbntpwd))
+                                       pw_buf.smb_nt_passwd = smbntpwd;
+                       }
+               }
+
+               fclose(fp);
+               pw_file_unlock(lockfd);
+               DEBUG(5, ("get_smbpwname: returning passwd entry for user %s, uid %d\n",
+                         user_name, uidval));
+               return &pw_buf;
+       }
+
+       fclose(fp);
+       pw_file_unlock(lockfd);
+       return NULL;
+}
+#else
+void 
+smbpass_dummy(void)
+{
+}                              /* To avoid compiler complaints */
+#endif
diff --git a/source/printing/pcap.c b/source/printing/pcap.c
new file mode 100644 (file)
index 0000000..8973b16
--- /dev/null
@@ -0,0 +1,383 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   printcap parsing
+   Copyright (C) Karl Auer 1993,1994
+
+   Re-working by Martin Kiff, 1994
+   
+   Re-written again by Andrew Tridgell
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the 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.
+ */
+
+#include "includes.h"
+
+#include "smb.h"
+#include "loadparm.h"
+#include "pcap.h"
+
+extern int DEBUGLEVEL;
+
+#ifdef AIX
+/*  ******************************************
+     Extend for AIX system and qconfig file
+       from 'boulard@univ-rennes1.fr
+    ****************************************** */
+static int strlocate(char *xpLine,char *xpS)
+{
+       int iS,iL,i,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)())
+{
+       int iLg,iEtat;
+       FILE *pfile;
+       char *line,*p;
+       pstring name,comment;
+       line  = NULL;
+       *name = 0;
+       *comment = 0;
+
+       if ((pfile = fopen(psz, "r")) == 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)); free(line))
+       {
+               if (*line == '*' || *line == 0)
+               continue;
+               switch (iEtat)
+               {
+                       case 0: /* locate an entry */
+                        if (*line == '\t' || *line == ' ') continue;
+                        if ((p=strchr(line,':')))
+                        {
+                               *p = '\0';
+                               p = strtok(line,":");
+                               if (strcmp(p,"bsh")!=0)
+                                 {
+                                   strcpy(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;
+               }
+       }
+       fclose(pfile);
+}
+
+/* Scan qconfig file and locate de printername */
+
+static BOOL ScanQconfig(char *psz,char *pszPrintername)
+{
+       int iLg,iEtat;
+       FILE *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 = fopen(psz, "r")) == NULL)
+       {
+             DEBUG(0,( "Unable to open qconfig file %s for read!\n", psz));
+             free(pName);
+             return(False);
+       }
+       sprintf(pName,"%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)); 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);
+                          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);
+                               free(pName);
+                               fclose(pfile);
+                               return(True);
+                         }
+                         break;
+               }
+       }
+       free (pName);
+       fclose(pfile);
+       return(False);
+}
+
+#endif
+/***************************************************************************
+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(char *pszPrintername, char *pszPrintcapname)
+{
+  char *line=NULL;
+  char *psz;
+  char *p,*q;
+  FILE *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 AIX
+  if (strlocate(psz,"/qconfig") != NULL)
+     return(ScanQconfig(psz,pszPrintername));
+#endif
+  if ((pfile = fopen(psz, "r")) == NULL)
+    {
+      DEBUG(0,( "Unable to open printcap file %s for read!\n", psz));
+      return(False);
+    }
+
+  for (;(line = fgets_slash(NULL,sizeof(pstring),pfile)); free(line))
+    {
+      if (*line == '#' || *line == 0)
+       continue;
+
+      /* now we have a real printer line - cut it off at the first : */      
+      p = strchr(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(p,'|'))) *q++ = 0;
+
+         if (strequal(p,pszPrintername))
+           {
+             /* normalise the case */
+             strcpy(pszPrintername,p);
+             free(line);
+             fclose(pfile);
+             return(True);           
+           }
+         p = q;
+       }
+    }
+
+
+  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)
+***************************************************************************/
+void pcap_printer_fn(void (*fn)())
+{
+  pstring name,comment;
+  char *line;
+  char *psz;
+  char *p,*q;
+  FILE *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 AIX
+  if (strlocate(psz,"/qconfig") != NULL)
+  {
+       ScanQconfig_fn(psz,fn);
+     return;
+  }
+#endif
+  if ((pfile = fopen(psz, "r")) == NULL)
+    {
+      DEBUG(0,( "Unable to open printcap file %s for read!\n", psz));
+      return;
+    }
+
+  for (;(line = fgets_slash(NULL,sizeof(pstring),pfile)); free(line))
+    {
+      if (*line == '#' || *line == 0)
+       continue;
+
+      /* now we have a real printer line - cut it off at the first : */      
+      p = strchr(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(p,'|'))) *q++ = 0;
+
+         has_punctuation = (strchr(p,' ') || strchr(p,'(') || strchr(p,')'));
+
+         if (strlen(p)>strlen(comment) && has_punctuation)
+           {
+             StrnCpy(comment,p,sizeof(comment)-1);
+             continue;
+           }
+
+         if (strlen(p) <= 8 && strlen(p)>strlen(name) && !has_punctuation)
+           {
+             if (!*comment) strcpy(comment,name);
+             strcpy(name,p);
+             continue;
+           }
+
+         if (!strchr(comment,' ') && 
+             strlen(p) > strlen(comment))
+           {
+             StrnCpy(comment,p,sizeof(comment)-1);
+             continue;
+           }
+       }
+
+      comment[60] = 0;
+      name[8] = 0;
+
+      if (*name)
+       fn(name,comment);
+    }
+  fclose(pfile);
+}
diff --git a/source/printing/printing.c b/source/printing/printing.c
new file mode 100644 (file)
index 0000000..1dd8921
--- /dev/null
@@ -0,0 +1,859 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   printing routines
+   Copyright (C) Andrew Tridgell 1992-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"
+#include "loadparm.h"
+extern int DEBUGLEVEL;
+extern connection_struct Connections[];
+extern files_struct Files[];
+
+static BOOL * lpq_cache_reset=NULL;
+
+static int check_lpq_cache(int snum) {
+  static int lpq_caches=0;
+  
+  if (lpq_caches <= snum) {
+      BOOL * p;
+      p = (BOOL *) Realloc(lpq_cache_reset,(snum+1)*sizeof(BOOL));
+      if (p) {
+        lpq_cache_reset=p;
+        lpq_caches = snum+1;
+      }
+  }
+  return lpq_caches;
+}
+
+void lpq_reset(int snum)
+{
+  if (check_lpq_cache(snum) > snum) lpq_cache_reset[snum]=True;
+}
+
+
+/****************************************************************************
+Build the print command in the supplied buffer. This means getting the
+print command for the service and inserting the printer name and the
+print file name. Return NULL on error, else the passed buffer pointer.
+****************************************************************************/
+static char *build_print_command(int cnum, char *command, char *syscmd, char *filename1)
+{
+  int snum = SNUM(cnum);
+  char *tstr;
+  pstring filename;
+  
+  /* get the print command for the service. */
+  tstr = command;
+  if (!syscmd || !tstr) {
+    DEBUG(0,("No print command for service `%s'\n", SERVICE(snum)));
+    return (NULL);
+  }
+
+  /* copy the command into the buffer for extensive meddling. */
+  StrnCpy(syscmd, tstr, sizeof(pstring) - 1);
+  
+  /* look for "%s" in the string. If there is no %s, we cannot print. */   
+  if (!strstr(syscmd, "%s") && !strstr(syscmd, "%f")) {
+    DEBUG(2,("WARNING! No placeholder for the filename in the print command for service %s!\n", SERVICE(snum)));
+  }
+  
+  if (strstr(syscmd,"%s")) {
+    int iOffset = strstr(syscmd, "%s") - syscmd;
+    
+    /* construct the full path for the filename, shouldn't be necessary unless
+       the subshell causes a "cd" to be executed.
+       Only use the full path if there isn't a / preceding the %s */
+    if (iOffset==0 || syscmd[iOffset-1] != '/') {
+      StrnCpy(filename,Connections[cnum].connectpath,sizeof(filename)-1);
+      trim_string(filename,"","/");
+      strcat(filename,"/");
+      strcat(filename,filename1);
+    }
+    else
+      strcpy(filename,filename1);
+    
+    string_sub(syscmd, "%s", filename);
+  }
+  
+  string_sub(syscmd, "%f", filename1);
+  
+  /* Does the service have a printername? If not, make a fake and empty    */
+  /* printer name. That way a %p is treated sanely if no printer */
+  /* name was specified to replace it. This eventuality is logged.         */
+  tstr = PRINTERNAME(snum);
+  if (tstr == NULL || tstr[0] == '\0') {
+    DEBUG(3,( "No printer name - using %s.\n", SERVICE(snum)));
+    tstr = SERVICE(snum);
+  }
+  
+  string_sub(syscmd, "%p", tstr);
+  
+  standard_sub(cnum,syscmd);
+  
+  return (syscmd);
+}
+
+
+/****************************************************************************
+print a file - called on closing the file
+****************************************************************************/
+void print_file(int fnum)
+{
+  pstring syscmd;
+  int cnum = Files[fnum].cnum;
+  int snum=SNUM(cnum);
+  char *tempstr;
+
+  *syscmd = 0;
+
+  if (file_size(Files[fnum].name) <= 0) {
+    DEBUG(3,("Discarding null print job %s\n",Files[fnum].name));
+    sys_unlink(Files[fnum].name);
+    return;
+  }
+
+  tempstr = build_print_command(cnum, PRINTCOMMAND(snum), syscmd, Files[fnum].name);
+  if (tempstr != NULL)
+    {
+      int ret = smbrun(syscmd,NULL);
+      DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));
+    }
+  else
+    DEBUG(0,("Null print command?\n"));
+  
+  lpq_reset(snum);
+}
+
+static char *Months[13] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+                             "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Err"};
+
+
+/*******************************************************************
+process time fields
+********************************************************************/
+static time_t EntryTime(string tok[], int ptr, int count, int minimum)
+{
+  time_t jobtime;
+
+  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;
+      jobtime = mktime(t);
+    }
+  }
+  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
+****************************************************************************/
+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 5
+#define        NTOK    6
+#else  /* OSF1 */
+#define        RANKTOK 0
+#define        USERTOK 1
+#define        JOBTOK  2
+#define        FILETOK 3
+#define        TOTALTOK 4
+#define        NTOK    5
+#endif /* OSF1 */
+
+  string tok[NTOK];
+  int count=0;
+
+#ifdef OSF1
+  int length;
+  length = strlen(line);
+  if (line[length-3] == ':')
+       return(False);
+#endif /* OSF1 */
+
+  /* handle the case of "(standard input)" as a filename */
+  string_sub(line,"standard input","STDIN");
+  string_sub(line,"(","\"");
+  string_sub(line,")","\"");
+  
+  for (count=0; count<NTOK && next_token(&line,tok[count],NULL); count++) ;
+
+  /* we must get NTOK tokens */
+  if (count < NTOK)
+    return(False);
+
+  /* the Job and Total columns must be integer */
+  if (!isdigit(*tok[JOBTOK]) || !isdigit(*tok[TOTALTOK])) return(False);
+
+  /* if the fname contains a space then use STDIN */
+  if (strchr(tok[FILETOK],' '))
+    strcpy(tok[FILETOK],"STDIN");
+
+  /* only take the last part of the filename */
+  {
+    string tmp;
+    char *p = strrchr(tok[FILETOK],'/');
+    if (p)
+      {
+       strcpy(tmp,p+1);
+       strcpy(tok[FILETOK],tmp);
+      }
+  }
+       
+
+  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->user,tok[USERTOK],sizeof(buf->user)-1);
+  StrnCpy(buf->file,tok[FILETOK],sizeof(buf->file)-1);
+#ifdef PRIOTOK
+  buf->priority = atoi(tok[PRIOTOK]);
+#else
+  buf->priority = 1;
+#endif
+  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)
+{
+  string tok[11];
+  int count=0;
+
+  /* handle the case of "(standard input)" as a filename */
+  string_sub(line,"standard input","STDIN");
+  string_sub(line,"(","\"");
+  string_sub(line,")","\"");
+
+  for (count=0; count<10 && next_token(&line,tok[count],NULL); count++) ;
+
+  /* we must get 6 tokens */
+  if (count < 10)
+  {
+      if ((count == 7) && (strcmp(tok[0],"QUEUED") == 0))
+      {
+          /* the 2nd and 5th columns must be integer */
+          if (!isdigit(*tok[1]) || !isdigit(*tok[4])) return(False);
+          buf->size = atoi(tok[4]) * 1024;
+          /* if the fname contains a space then use STDIN */
+          if (strchr(tok[2],' '))
+            strcpy(tok[2],"STDIN");
+
+          /* only take the last part of the filename */
+          {
+            string tmp;
+            char *p = strrchr(tok[2],'/');
+            if (p)
+              {
+                strcpy(tmp,p+1);
+                strcpy(tok[2],tmp);
+              }
+          }
+
+
+          buf->job = atoi(tok[1]);
+          buf->status = LPQ_QUEUED;
+         buf->priority = 0;
+          buf->time = time(NULL);
+          StrnCpy(buf->user,tok[3],sizeof(buf->user)-1);
+          StrnCpy(buf->file,tok[2],sizeof(buf->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(*tok[3]) || !isdigit(*tok[8])) return(False);
+      buf->size = atoi(tok[8]) * 1024;
+      /* if the fname contains a space then use STDIN */
+      if (strchr(tok[4],' '))
+        strcpy(tok[4],"STDIN");
+
+      /* only take the last part of the filename */
+      {
+        string tmp;
+        char *p = strrchr(tok[4],'/');
+        if (p)
+          {
+            strcpy(tmp,p+1);
+            strcpy(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->user,tok[5],sizeof(buf->user)-1);
+      StrnCpy(buf->file,tok[4],sizeof(buf->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 string 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 TAB = '\011';  
+  string tok[12];
+
+  /* If a line begins with a horizontal TAB, it is a subline type */
+  
+  if (line[0] == TAB) { /* 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");
+    string_sub(line,"(","\"");
+    string_sub(line,")","\"");
+    
+    for (count=0; count<2 && next_token(&line,tok[count],NULL); count++) ;
+    /* we must get 2 tokens */
+    if (count < 2) return(False);
+    
+    /* the 2nd column must be integer */
+    if (!isdigit(*tok[1])) return(False);
+    
+    /* if the fname contains a space then use STDIN */
+    if (strchr(tok[0],' '))
+      strcpy(tok[0],"STDIN");
+    
+    buf->size = atoi(tok[1]);
+    StrnCpy(buf->file,tok[0],sizeof(buf->file)-1);
+    
+    /* fill things from header line */
+    buf->time = jobtime;
+    buf->job = jobid;
+    buf->status = jobstat;
+    buf->priority = jobprio;
+    StrnCpy(buf->user,jobuser,sizeof(buf->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,"-"," ");
+    
+    for (count=0; count<12 && next_token(&line,tok[count],NULL); 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(*tok[1]) || !isdigit(*tok[4]) || !isdigit(*tok[6])) return(False);
+    jobid = atoi(tok[1]);
+    StrnCpy(jobuser,tok[2],sizeof(buf->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 lpq 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)
+{
+  string tok[9];
+  int count=0;
+  char *p;
+
+  /* handle the dash in the job id */
+  string_sub(line,"-"," ");
+  
+  for (count=0; count<9 && next_token(&line,tok[count],NULL); count++) ;
+
+  /* we must get 7 tokens */
+  if (count < 7)
+    return(False);
+
+  /* the 2nd and 4th, 6th columns must be integer */
+  if (!isdigit(*tok[1]) || !isdigit(*tok[3])) return(False);
+  if (!isdigit(*tok[5])) return(False);
+
+  /* if the user contains a ! then trim the first part of it */  
+  if ((p=strchr(tok[2],'!')))
+    {
+      string tmp;
+      strcpy(tmp,p+1);
+      strcpy(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->user,tok[2],sizeof(buf->user)-1);
+  StrnCpy(buf->file,tok[2],sizeof(buf->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)
+{
+  string tok[7];
+  int count=0;
+
+  DEBUG(0,("antes [%s]\n", line));
+
+  /* handle the case of "-- standard input --" as a filename */
+  string_sub(line,"standard input","STDIN");
+  DEBUG(0,("despues [%s]\n", line));
+  string_sub(line,"-- ","\"");
+  string_sub(line," --","\"");
+  DEBUG(0,("despues 1 [%s]\n", line));
+
+  string_sub(line,"[job #","");
+  string_sub(line,"]","");
+  DEBUG(0,("despues 2 [%s]\n", line));
+
+  
+  
+  for (count=0; count<7 && next_token(&line,tok[count],NULL); count++) ;
+
+  /* we must get 7 tokens */
+  if (count < 7)
+    return(False);
+
+  /* the 3rd and 5th columns must be integer */
+  if (!isdigit(*tok[2]) || !isdigit(*tok[4])) return(False);
+
+  /* only take the last part of the filename */
+  {
+    string tmp;
+    char *p = strrchr(tok[6],'/');
+    if (p)
+      {
+       strcpy(tmp,p+1);
+       strcpy(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->user,tok[1],sizeof(buf->user)-1);
+  StrnCpy(buf->file,tok[6],sizeof(buf->file)-1);
+  return(True);
+}
+
+
+
+char *stat0_strings[] = { "enabled", "online", "idle", "no entries", "free", "ready", NULL };
+char *stat1_strings[] = { "offline", "disabled", "down", "off", "waiting", "no daemon", NULL };
+char *stat2_strings[] = { "jam", "paper", "error", "responding", "not accepting", "not running", "turned off", NULL };
+
+/****************************************************************************
+parse a lpq line. Choose printing style
+****************************************************************************/
+static BOOL parse_lpq_entry(int snum,char *line,
+                           print_queue_struct *buf,
+                           print_status_struct *status,BOOL first)
+{
+  BOOL ret;
+
+  switch (lp_printing())
+    {
+    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;
+    default:
+      ret = parse_lpq_bsd(line,buf,first);
+      break;
+    }
+
+#ifdef LPQ_GUEST_TO_USER
+  if (ret) {
+    extern pstring sesssetup_user;
+    /* change guest entries to the current logged in user to make
+       them appear deletable to windows */
+    if (sesssetup_user[0] && strequal(buf->user,lp_guestaccount(snum)))
+      strcpy(buf->user,sesssetup_user);
+  }
+#endif
+
+  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;
+         }
+      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;
+         }
+      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;
+         }
+       break;
+      }
+    }
+
+  return(ret);
+}
+
+/****************************************************************************
+get a printer queue
+****************************************************************************/
+int get_printqueue(int snum,int cnum,print_queue_struct **queue,
+                  print_status_struct *status)
+{
+  char *lpq_command = lp_lpqcommand(snum);
+  char *printername = PRINTERNAME(snum);
+  int ret=0,count=0;
+  pstring syscmd;
+  fstring outfile;
+  pstring line;
+  FILE *f;
+  struct stat sbuf;
+  BOOL dorun=True;
+  int cachetime = lp_lpqcachetime();
+  int lfd = -1;
+
+  *line = 0;
+  check_lpq_cache(snum);
+  
+  if (!printername || !*printername)
+    {
+      DEBUG(6,("replacing printer name with service (snum=(%s,%d))\n",
+           lp_servicename(snum),snum));
+      printername = lp_servicename(snum);
+    }
+    
+  if (!lpq_command || !(*lpq_command))
+    {
+      DEBUG(5,("No lpq command\n"));
+      return(0);
+    }
+    
+  strcpy(syscmd,lpq_command);
+  string_sub(syscmd,"%p",printername);
+
+  standard_sub(cnum,syscmd);
+
+  sprintf(outfile,"/tmp/lpq.%08x",str_checksum(syscmd));
+  
+  if (!lpq_cache_reset[snum] && cachetime && !stat(outfile,&sbuf)) 
+    {
+      if (time(NULL) - sbuf.st_mtime < cachetime) {
+       DEBUG(3,("Using cached lpq output\n"));
+       dorun = False;
+      }
+
+      if (dorun) {
+       lfd = file_lock(outfile,LPQ_LOCK_TIMEOUT);
+       if (lfd<0 || 
+           (!fstat(lfd,&sbuf) && (time(NULL) - sbuf.st_mtime)<cachetime)) {
+         DEBUG(3,("Using cached lpq output\n"));
+         dorun = False;
+         file_unlock(lfd); lfd = -1;
+       }
+      }
+    }
+
+  if (dorun) {
+    ret = smbrun(syscmd,outfile);
+    DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));
+  }
+
+  lpq_cache_reset[snum] = False;
+
+  f = fopen(outfile,"r");
+  if (!f) {
+    if (lfd >= 0) file_unlock(lfd);
+    return(0);
+  }
+
+  if (status) {
+    strcpy(status->message,"");
+    status->status = LPSTAT_OK;
+  }
+      
+  while (fgets(line,sizeof(pstring),f))
+    {
+      DEBUG(6,("QUEUE2: %s\n",line));
+
+      *queue = Realloc(*queue,sizeof(print_queue_struct)*(count+1));
+      if (! *queue)
+       {
+         count = 0;
+         break;
+       }
+
+      bzero((char *)&(*queue)[count],sizeof(**queue));
+         
+      /* parse it */
+      if (!parse_lpq_entry(snum,line,&(*queue)[count],status,count==0))
+       continue;
+         
+      count++;
+    }        
+
+  fclose(f);
+
+  if (lfd >= 0) file_unlock(lfd);
+
+  if (!cachetime) 
+    unlink(outfile);
+  else
+    chmod(outfile,0666);
+  return(count);
+}
+
+
+/****************************************************************************
+delete a printer queue entry
+****************************************************************************/
+void del_printqueue(int cnum,int snum,int jobid)
+{
+  char *lprm_command = lp_lprmcommand(snum);
+  char *printername = PRINTERNAME(snum);
+  pstring syscmd;
+  char jobstr[20];
+  int ret;
+
+  if (!printername || !*printername)
+    {
+      DEBUG(6,("replacing printer name with service (snum=(%s,%d))\n",
+           lp_servicename(snum),snum));
+      printername = lp_servicename(snum);
+    }
+    
+  if (!lprm_command || !(*lprm_command))
+    {
+      DEBUG(5,("No lprm command\n"));
+      return;
+    }
+    
+  sprintf(jobstr,"%d",jobid);
+
+  strcpy(syscmd,lprm_command);
+  string_sub(syscmd,"%p",printername);
+  string_sub(syscmd,"%j",jobstr);
+  standard_sub(cnum,syscmd);
+
+  ret = smbrun(syscmd,NULL);
+  DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));  
+  lpq_reset(snum); /* queue has changed */
+}
+
+/****************************************************************************
+change status of a printer queue entry
+****************************************************************************/
+void status_printjob(int cnum,int snum,int jobid,int status)
+{
+  char *lpstatus_command = 
+    (status==LPQ_PAUSED?lp_lppausecommand(snum):lp_lpresumecommand(snum));
+  char *printername = PRINTERNAME(snum);
+  pstring syscmd;
+  char jobstr[20];
+  int ret;
+
+  if (!printername || !*printername)
+    {
+      DEBUG(6,("replacing printer name with service (snum=(%s,%d))\n",
+           lp_servicename(snum),snum));
+      printername = lp_servicename(snum);
+    }
+    
+  if (!lpstatus_command || !(*lpstatus_command))
+    {
+      DEBUG(5,("No lpstatus command to %s job\n",
+              (status==LPQ_PAUSED?"pause":"resume")));
+      return;
+    }
+    
+  sprintf(jobstr,"%d",jobid);
+
+  strcpy(syscmd,lpstatus_command);
+  string_sub(syscmd,"%p",printername);
+  string_sub(syscmd,"%j",jobstr);
+  standard_sub(cnum,syscmd);
+
+  ret = smbrun(syscmd,NULL);
+  DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));  
+  lpq_reset(snum); /* queue has changed */
+}
+
+
diff --git a/source/script/addtosmbpass b/source/script/addtosmbpass
new file mode 100644 (file)
index 0000000..42af518
--- /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:%s:%s:%s\n";
+  for(name in names) {
+    while ((getline < pwdf) > 0) {
+      if ($1 == name) {
+       printf(fmt, $1, $3, $5, $6, $7);
+       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, $6, $7);
+      }
+      close(command);
+    }
+  }
+}
+
diff --git a/source/script/installbin.sh b/source/script/installbin.sh
new file mode 100755 (executable)
index 0000000..633e6cb
--- /dev/null
@@ -0,0 +1,42 @@
+#!/bin/sh
+INSTALLPERMS=$1
+BASEDIR=$2
+BINDIR=$3
+LIBDIR=$4
+VARDIR=$5
+shift
+shift
+shift
+shift
+shift
+
+for d in $BASEDIR $BINDIR $LIBDIR $VARDIR; 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
+ echo Installing $p as $BINDIR/$p
+ if [ -f $BINDIR/$p ]; then
+   mv $BINDIR/$p $BINDIR/$p.old
+ fi
+ cp $p $BINDIR/$p
+ chmod $INSTALLPERMS $BINDIR/$p
+done
+
+
+cat << EOF
+======================================================================
+The binaries are installed. You may restore the old binaries (if there
+were any) using the command "make revert"
+======================================================================
+EOF
+
+exit 0
+
diff --git a/source/script/installman.sh b/source/script/installman.sh
new file mode 100755 (executable)
index 0000000..a79d157
--- /dev/null
@@ -0,0 +1,35 @@
+#!/bin/sh
+MANDIR=$1
+SRCDIR=$2
+
+echo Installing man pages in $MANDIR
+
+for d in $MANDIR $MANDIR/man1 $MANDIR/man5 $MANDIR/man7 $MANDIR/man8; do
+if [ ! -d $d ]; then
+mkdir $d
+if [ ! -d $d ]; then
+  echo Failed to make directory $d
+  exit 1
+fi
+fi
+done
+
+cp $SRCDIR../docs/*.1 $MANDIR/man1
+cp $SRCDIR../docs/*.5 $MANDIR/man5
+cp $SRCDIR../docs/*.8 $MANDIR/man8
+cp $SRCDIR../docs/*.7 $MANDIR/man7
+echo Setting permissions on man pages
+chmod 0644 $MANDIR/man1/smbstatus.1
+chmod 0644 $MANDIR/man1/smbclient.1
+chmod 0644 $MANDIR/man1/smbrun.1
+chmod 0644 $MANDIR/man1/testparm.1
+chmod 0644 $MANDIR/man1/testprns.1
+chmod 0644 $MANDIR/man1/smbtar.1
+chmod 0644 $MANDIR/man5/smb.conf.5
+chmod 0644 $MANDIR/man7/samba.7
+chmod 0644 $MANDIR/man8/smbd.8
+chmod 0644 $MANDIR/man8/nmbd.8
+
+echo Man pages installed
+exit 0
+
diff --git a/source/script/mksmbpasswd.sh b/source/script/mksmbpasswd.sh
new file mode 100755 (executable)
index 0000000..6e592ac
--- /dev/null
@@ -0,0 +1,6 @@
+#!/bin/sh
+awk 'BEGIN {FS=":"
+       printf("#\n# SMB password file.\n#\n")
+       }
+{ printf( "%s:%s:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:%s:%s:%s\n", $1, $3, $5, $6, $7) }
+'
diff --git a/source/script/revert.sh b/source/script/revert.sh
new file mode 100755 (executable)
index 0000000..68b47bf
--- /dev/null
@@ -0,0 +1,15 @@
+#!/bin/sh
+BINDIR=$1
+shift
+
+for p in $*; do
+ if [ -f $BINDIR/$p.old ]; then
+   echo Restoring $BINDIR/$p.old as $BINDIR/$p
+   mv $BINDIR/$p $BINDIR/$p.new
+   mv $BINDIR/$p.old $BINDIR/$p
+   rm -f $BINDIR/$p.new
+ fi
+done
+
+exit 0
+
diff --git a/source/script/smbtar b/source/script/smbtar
new file mode 100644 (file)
index 0000000..fc032ed
--- /dev/null
@@ -0,0 +1,141 @@
+#!/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)
+
+case $0 in
+    # when called by absolute path, assume smbclient is in the same directory
+    /*)
+       SMBCLIENT="`dirname $0`/smbclient";;
+    *)  # 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=""
+blocksize=""
+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
+  -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
+}
+
+while getopts rivl: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}g
+      ;;
+   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
+        tarargs=${tarargs}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="blocksize $OPTARG"
+      case "$OPTARG" in
+       [0-9]*) ;;
+       *)      echo >&2 "$0: Error, block size not numeric: -b $OPTARG"
+               exit 1
+      esac
+      tarargs=${tarargs}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
+
+eval $SMBCLIENT "'\\\\$server\\$service'" "'$password'" -U "'$username'" \
+-E -N $log -D "'$cdcmd'" \
+-T${tarcmd}${tarargs} $blocksize $newer $tapefile $* $verbose
+
+
diff --git a/source/script/updatesmbpasswd.sh b/source/script/updatesmbpasswd.sh
new file mode 100755 (executable)
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/source/smbd/chgpasswd.c b/source/smbd/chgpasswd.c
new file mode 100644 (file)
index 0000000..dc0514c
--- /dev/null
@@ -0,0 +1,376 @@
+/* fork a child process to exec passwd and write to its
+* tty to change a users password. This is running as the
+* user who is attempting to change the password.
+*/
+
+/* 
+ * This code was copied/borrowed and stolen from various sources.
+ * The primary source was the poppasswd.c from the authors of POPMail. This software
+ * was included as a client to change passwords using the 'passwd' program
+ * on the remote machine.
+ *
+ * This routine is called by set_user_password() in password.c only if ALLOW_PASSWORD_CHANGE
+ * is defined in the compiler directives located in the Makefile.
+ *
+ * This code has been hacked by Bob Nance (nance@niehs.nih.gov) and Evan Patterson
+ * (patters2@niehs.nih.gov) at the National Institute of Environmental Health Sciences
+ * and rights to modify, distribute or incorporate this change to the CAP suite or
+ * using it for any other reason are granted, so long as this disclaimer is left intact.
+ */
+
+/*
+   This code was hacked considerably for inclusion in Samba, primarily
+   by Andrew.Tridgell@anu.edu.au. The biggest change was the addition
+   of the "password chat" option, which allows the easy runtime
+   specification of the expected sequence of events to change a
+   password.
+   */
+
+#include "includes.h"
+#include "loadparm.h"
+
+extern int DEBUGLEVEL;
+
+#ifdef ALLOW_CHANGE_PASSWORD
+
+#define MINPASSWDLENGTH 5
+#define BUFSIZE 512
+
+static int findpty(char **slave)
+{
+  int master;
+#ifdef SVR4
+  extern char *ptsname();
+#else
+  static char line[12] = "/dev/ptyXX";
+  void *dirp;
+  char *dpname;
+#endif
+  
+#ifdef SVR4
+  if ((master = open("/dev/ptmx", O_RDWR)) >= 1) {
+    grantpt(master);
+    unlockpt(master);
+    *slave = ptsname(master);
+    return (master);
+  }
+#else
+  dirp = OpenDir("/dev");
+  if (!dirp) return(-1);
+  while ((dpname = ReadDirName(dirp)) != NULL) {
+    if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5) {
+      line[8] = dpname[3];
+      line[9] = dpname[4];
+      if ((master = open(line, O_RDWR)) >= 0) {
+       line[5] = 't';
+       *slave = line;
+       CloseDir(dirp);
+       return (master);
+      }
+    }
+  }
+  CloseDir(dirp);
+#endif
+  return (-1);
+}
+
+static int dochild(int master,char *slavedev, char *name, char *passwordprogram)
+{
+  int slave;
+  struct termios stermios;
+  struct passwd *pass = Get_Pwnam(name,True);
+  int gid = pass->pw_gid;
+  int uid = pass->pw_uid;
+
+#ifdef USE_SETRES
+  setresuid(0,0,0);
+#else
+  setuid(0);
+#endif
+
+  /* Start new session - gets rid of controlling terminal. */
+  if (setsid() < 0) {
+    DEBUG(3,("Weirdness, couldn't let go of controlling terminal\n"));
+    return(False);
+  }
+
+  /* Open slave pty and acquire as new controlling terminal. */
+  if ((slave = open(slavedev, O_RDWR)) < 0) {
+    DEBUG(3,("More weirdness, could not open %s\n", 
+            slavedev));
+    return(False);
+  }
+#ifdef SVR4
+  ioctl(slave, I_PUSH, "ptem");
+  ioctl(slave, I_PUSH, "ldterm");
+#else
+  if (ioctl(slave,TIOCSCTTY,0) <0) {
+     DEBUG(3,("Error in ioctl call for slave pty\n"));
+     /* return(False); */
+  }
+#endif
+
+  /* Close master. */
+  close(master);
+
+  /* Make slave stdin/out/err of child. */
+
+  if (dup2(slave, STDIN_FILENO) != STDIN_FILENO) {
+    DEBUG(3,("Could not re-direct stdin\n"));
+    return(False);
+  }
+  if (dup2(slave, STDOUT_FILENO) != STDOUT_FILENO) {
+    DEBUG(3,("Could not re-direct stdout\n"));
+    return(False);
+  }
+  if (dup2(slave, STDERR_FILENO) != STDERR_FILENO) {
+    DEBUG(3,("Could not re-direct stderr\n"));
+    return(False);
+  }
+  if (slave > 2) close(slave);
+
+  /* Set proper terminal attributes - no echo, canonical input processing,
+     no map NL to CR/NL on output. */
+
+  if (tcgetattr(0, &stermios) < 0) {
+    DEBUG(3,("could not read default terminal attributes on pty\n"));
+    return(False);
+  }
+  stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
+  stermios.c_lflag |= ICANON;
+  stermios.c_oflag &= ~(ONLCR);
+  if (tcsetattr(0, TCSANOW, &stermios) < 0) {
+    DEBUG(3,("could not set attributes of pty\n"));
+    return(False);
+  }
+
+  /* make us completely into the right uid */
+#ifdef USE_SETRES
+  setresgid(0,0,0);
+  setresuid(0,0,0);
+  setresgid(gid,gid,gid);
+  setresuid(uid,uid,uid);      
+#else      
+  setuid(0);
+  seteuid(0);
+  setgid(gid);
+  setegid(gid);
+  setuid(uid);
+  seteuid(uid);
+#endif
+
+  /* execl() password-change application */
+  if (execl("/bin/sh","sh","-c",passwordprogram,NULL) < 0) {
+    DEBUG(3,("Bad status returned from %s\n",passwordprogram));
+    return(False);
+  }
+  return(True);
+}
+
+static int expect(int master,char *expected,char *buf)
+{
+  int n, m;
+  n = 0;
+  buf[0] = 0;
+  while (1) {
+    if (n >= BUFSIZE-1) {
+      return False;
+    }
+
+    /* allow 4 seconds for some output to appear */
+    m = read_with_timeout(master, buf+n, 1, BUFSIZE-1-n, 4000, True);
+    if (m < 0) 
+      return False;
+
+    n += m;
+    buf[n] = 0;
+
+    {
+      pstring s1,s2;
+      strcpy(s1,buf);
+      strcpy(s2,expected);
+      if (do_match(s1, s2, False))
+       return(True);
+    }
+  }
+}
+
+static void pwd_sub(char *buf)
+{
+  string_sub(buf,"\\n","\n");
+  string_sub(buf,"\\r","\r");
+  string_sub(buf,"\\s"," ");
+  string_sub(buf,"\\t","\t");
+}
+
+static void writestring(int fd,char *s)
+{
+  int l;
+  
+  l = strlen (s);
+  write (fd, s, l);
+}
+
+
+static int talktochild(int master, char *chatsequence)
+{
+  char buf[BUFSIZE];
+  int count=0;
+  char *ptr=chatsequence;
+  fstring chatbuf;
+
+  *buf = 0;
+  sleep(1);
+
+  while (next_token(&ptr,chatbuf,NULL)) {
+    BOOL ok=True;
+    count++;
+    pwd_sub(chatbuf);
+    if (!strequal(chatbuf,"."))
+      ok = expect(master,chatbuf,buf);
+
+#if DEBUG_PASSWORD
+      DEBUG(100,("chatbuf=[%s] responsebuf=[%s]\n",chatbuf,buf));
+#endif      
+
+    if (!ok) {
+      DEBUG(3,("response %d incorrect\n",count));
+      return(False);
+    }
+
+    if (!next_token(&ptr,chatbuf,NULL)) break;
+    pwd_sub(chatbuf);
+    if (!strequal(chatbuf,"."))
+      writestring(master,chatbuf);
+
+#if DEBUG_PASSWORD
+    DEBUG(100,("sendbuf=[%s]\n",chatbuf));
+#endif      
+  }
+
+  if (count<1) return(False);
+
+  return (True);
+}
+
+
+BOOL chat_with_program(char *passwordprogram,char *name,char *chatsequence)
+{
+  char *slavedev;
+  int master;
+  pid_t pid, wpid;
+  int wstat;
+  BOOL chstat;    
+
+  /* allocate a pseudo-terminal device */
+  if ((master = findpty (&slavedev)) < 0) {
+    DEBUG(3,("Cannot Allocate pty for password change: %s",name));
+    return(False);
+  }
+
+  if ((pid = fork()) < 0) {
+    DEBUG(3,("Cannot fork() child for password change: %s",name));
+    return(False);
+  }
+
+  /* we now have a pty */
+  if (pid > 0){                        /* This is the parent process */
+    if ((chstat = talktochild(master, chatsequence)) == False) {
+      DEBUG(3,("Child failed to change password: %s\n",name));
+      kill(pid, SIGKILL); /* be sure to end this process */
+      return(False);
+    }
+    if ((wpid = waitpid(pid, &wstat, 0)) < 0) {
+      DEBUG(3,("The process is no longer waiting!\n\n"));
+      return(False);
+    }
+    if (pid != wpid) {
+      DEBUG(3,("We were waiting for the wrong process ID\n")); 
+      return(False);
+    }
+    if (WIFEXITED(wstat) == 0) {
+      DEBUG(3,("The process exited while we were waiting\n"));
+      return(False);
+    }
+    if (WEXITSTATUS(wstat) != 0) {
+      DEBUG(3,("The status of the process exiting was %d\n", wstat));
+      return(False);
+    }
+    
+  } else {
+    /* CHILD */
+
+    /* make sure it doesn't freeze */
+    alarm(20);
+
+    DEBUG(3,("Dochild for user %s (uid=%d,gid=%d)\n",name,getuid(),getgid()));
+    chstat = dochild(master, slavedev, name, passwordprogram);
+  }
+  DEBUG(3,("Password change %ssuccessful for user %s\n", (chstat?"":"un"), name));
+  return (chstat);
+}
+
+
+BOOL chgpasswd(char *name,char *oldpass,char *newpass)
+{
+  pstring passwordprogram;
+  pstring chatsequence;
+
+  strlower(name); 
+  DEBUG(3,("Password change for user: %s\n",name));
+
+#if DEBUG_PASSWORD
+  DEBUG(100,("Passwords: old=%s new=%s\n",oldpass,newpass)); 
+#endif
+
+  /* Take the passed information and test it for minimum criteria */
+  /* Minimum password length */
+  if (strlen(newpass) < MINPASSWDLENGTH) /* too short, must be at least MINPASSWDLENGTH */ 
+    {
+      DEBUG(2,("Password Change: %s, New password is shorter than MINPASSWDLENGTH\n",name));
+      return (False);          /* inform the user */
+    }
+  
+  /* Password is same as old password */
+  if (strcmp(oldpass,newpass) == 0) /* don't allow same password */
+    {
+      DEBUG(2,("Password Change: %s, New password is same as old\n",name)); /* log the attempt */
+      return (False);          /* inform the user */
+    }
+
+#if (defined(PASSWD_PROGRAM) && defined(PASSWD_CHAT))
+  strcpy(passwordprogram,PASSWD_PROGRAM);
+  strcpy(chatsequence,PASSWD_CHAT);
+#else
+  strcpy(passwordprogram,lp_passwd_program());
+  strcpy(chatsequence,lp_passwd_chat());
+#endif
+
+  if (!*chatsequence) {
+    DEBUG(2,("Null chat sequence - no password changing\n"));
+    return(False);
+  }
+
+  if (!*passwordprogram) {
+    DEBUG(2,("Null password program - no password changing\n"));
+    return(False);
+  }
+
+  string_sub(passwordprogram,"%u",name);
+  string_sub(passwordprogram,"%o",oldpass);
+  string_sub(passwordprogram,"%n",newpass);
+
+  string_sub(chatsequence,"%u",name);
+  string_sub(chatsequence,"%o",oldpass);
+  string_sub(chatsequence,"%n",newpass);
+  return(chat_with_program(passwordprogram,name,chatsequence));
+}
+
+#else
+BOOL chgpasswd(char *name,char *oldpass,char *newpass)
+{
+  DEBUG(0,("Password changing not compiled in (user=%s)\n",name));
+  return(False);
+}
+#endif
diff --git a/source/smbd/dir.c b/source/smbd/dir.c
new file mode 100644 (file)
index 0000000..ac6f918
--- /dev/null
@@ -0,0 +1,955 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   Directory handling routines
+   Copyright (C) Andrew Tridgell 1992-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"
+#include "loadparm.h"
+
+extern int DEBUGLEVEL;
+extern connection_struct Connections[];
+
+/*
+   This module implements directory related functions for Samba.
+*/
+
+
+
+uint32 dircounter = 0;
+
+
+#define NUMDIRPTRS 256
+
+
+static struct dptr_struct
+{
+  int pid;
+  int cnum;
+  uint32 lastused;
+  void *ptr;
+  BOOL valid;
+  BOOL finished;
+  BOOL expect_close;
+  char *wcard; /* Field only used for lanman2 trans2_findfirst/next searches */
+  uint16 attr; /* Field only used for lanman2 trans2_findfirst/next searches */
+  char *path;
+}
+dirptrs[NUMDIRPTRS];
+
+
+static int dptrs_open = 0;
+
+/****************************************************************************
+initialise the dir array
+****************************************************************************/
+void init_dptrs(void)
+{
+  static BOOL dptrs_init=False;
+  int i;
+
+  if (dptrs_init) return;
+  for (i=0;i<NUMDIRPTRS;i++)    
+    {
+      dirptrs[i].valid = False;
+      dirptrs[i].wcard = NULL;
+      dirptrs[i].ptr = NULL;
+      string_init(&dirptrs[i].path,"");
+    }
+  dptrs_init = True;
+}
+
+/****************************************************************************
+idle a dptr - the directory is closed but the control info is kept
+****************************************************************************/
+static void dptr_idle(int key)
+{
+  if (dirptrs[key].valid && dirptrs[key].ptr) {
+    DEBUG(4,("Idling dptr key %d\n",key));
+    dptrs_open--;
+    CloseDir(dirptrs[key].ptr);
+    dirptrs[key].ptr = NULL;
+  }    
+}
+
+/****************************************************************************
+idle the oldest dptr
+****************************************************************************/
+static void dptr_idleoldest(void)
+{
+  int i;
+  uint32 old=dircounter+1;
+  int oldi= -1;
+  for (i=0;i<NUMDIRPTRS;i++)
+    if (dirptrs[i].valid && dirptrs[i].ptr && dirptrs[i].lastused < old) {
+      old = dirptrs[i].lastused;
+      oldi = i;
+    }
+  if (oldi != -1)
+    dptr_idle(oldi);
+  else
+    DEBUG(0,("No dptrs available to idle??\n"));
+}
+
+/****************************************************************************
+get the dir ptr for a dir index
+****************************************************************************/
+static void *dptr_get(int key,uint32 lastused)
+{
+  if (dirptrs[key].valid) {
+    if (lastused) dirptrs[key].lastused = lastused;
+    if (!dirptrs[key].ptr) {
+      if (dptrs_open >= MAXDIR)
+       dptr_idleoldest();
+      DEBUG(4,("Reopening dptr key %d\n",key));
+      if ((dirptrs[key].ptr = OpenDir(dirptrs[key].path)))
+       dptrs_open++;
+    }
+    return(dirptrs[key].ptr);
+  }
+  return(NULL);
+}
+
+/****************************************************************************
+get the dir path for a dir index
+****************************************************************************/
+char *dptr_path(int key)
+{
+  if (dirptrs[key].valid)
+    return(dirptrs[key].path);
+  return(NULL);
+}
+
+/****************************************************************************
+get the dir wcard for a dir index (lanman2 specific)
+****************************************************************************/
+char *dptr_wcard(int key)
+{
+  if (dirptrs[key].valid)
+    return(dirptrs[key].wcard);
+  return(NULL);
+}
+
+/****************************************************************************
+set the dir wcard for a dir index (lanman2 specific)
+Returns 0 on ok, 1 on fail.
+****************************************************************************/
+BOOL dptr_set_wcard(int key, char *wcard)
+{
+  if (dirptrs[key].valid) {
+    dirptrs[key].wcard = wcard;
+    return True;
+  }
+  return False;
+}
+
+/****************************************************************************
+set the dir attrib for a dir index (lanman2 specific)
+Returns 0 on ok, 1 on fail.
+****************************************************************************/
+BOOL dptr_set_attr(int key, uint16 attr)
+{
+  if (dirptrs[key].valid) {
+    dirptrs[key].attr = attr;
+    return True;
+  }
+  return False;
+}
+
+/****************************************************************************
+get the dir attrib for a dir index (lanman2 specific)
+****************************************************************************/
+uint16 dptr_attr(int key)
+{
+  if (dirptrs[key].valid)
+    return(dirptrs[key].attr);
+  return(0);
+}
+
+/****************************************************************************
+close a dptr
+****************************************************************************/
+void dptr_close(int key)
+{
+  if (dirptrs[key].valid) {
+    DEBUG(4,("closing dptr key %d\n",key));
+    if (dirptrs[key].ptr) {
+      CloseDir(dirptrs[key].ptr);
+      dptrs_open--;
+    }
+    /* Lanman 2 specific code */
+    if (dirptrs[key].wcard)
+      free(dirptrs[key].wcard);
+    dirptrs[key].valid = False;
+    string_set(&dirptrs[key].path,"");
+  }
+}
+
+/****************************************************************************
+close all dptrs for a cnum
+****************************************************************************/
+void dptr_closecnum(int cnum)
+{
+  int i;
+  for (i=0;i<NUMDIRPTRS;i++)
+    if (dirptrs[i].valid && dirptrs[i].cnum == cnum)
+      dptr_close(i);
+}
+
+/****************************************************************************
+idle all dptrs for a cnum
+****************************************************************************/
+void dptr_idlecnum(int cnum)
+{
+  int i;
+  for (i=0;i<NUMDIRPTRS;i++)
+    if (dirptrs[i].valid && dirptrs[i].cnum == cnum && dirptrs[i].ptr)
+      dptr_idle(i);
+}
+
+/****************************************************************************
+close a dptr that matches a given path, only if it matches the pid also
+****************************************************************************/
+void dptr_closepath(char *path,int pid)
+{
+  int i;
+  for (i=0;i<NUMDIRPTRS;i++)
+    if (dirptrs[i].valid && pid == dirptrs[i].pid &&
+       strequal(dirptrs[i].path,path))
+      dptr_close(i);
+}
+
+/****************************************************************************
+  start a directory listing
+****************************************************************************/
+static BOOL start_dir(int cnum,char *directory)
+{
+  DEBUG(5,("start_dir cnum=%d dir=%s\n",cnum,directory));
+
+  if (!check_name(directory,cnum))
+    return(False);
+  
+  if (! *directory)
+    directory = ".";
+
+  Connections[cnum].dirptr = OpenDir(directory);
+  if (Connections[cnum].dirptr) {    
+    dptrs_open++;
+    string_set(&Connections[cnum].dirpath,directory);
+    return(True);
+  }
+  
+  return(False);
+}
+
+
+/****************************************************************************
+create a new dir ptr
+****************************************************************************/
+int dptr_create(int cnum,char *path, BOOL expect_close,int pid)
+{
+  int i;
+  uint32 old;
+  int oldi;
+
+  if (!start_dir(cnum,path))
+    return(-1);
+
+  if (dptrs_open >= MAXDIR)
+    dptr_idleoldest();
+
+  for (i=0;i<NUMDIRPTRS;i++)
+    if (!dirptrs[i].valid)
+      break;
+  if (i == NUMDIRPTRS) i = -1;
+
+
+  /* as a 2nd option, grab the oldest not marked for expect_close */
+  if (i == -1) {
+    old=dircounter+1;
+    oldi= -1;
+    for (i=0;i<NUMDIRPTRS;i++)
+      if (!dirptrs[i].expect_close && dirptrs[i].lastused < old) {
+       old = dirptrs[i].lastused;
+       oldi = i;
+      }
+    i = oldi;
+  }
+
+  /* a 3rd option - grab the oldest one */
+  if (i == -1) {
+    old=dircounter+1;
+    oldi= -1;
+    for (i=0;i<NUMDIRPTRS;i++)
+      if (dirptrs[i].lastused < old) {
+       old = dirptrs[i].lastused;
+       oldi = i;
+      }
+    i = oldi;
+  }
+
+  if (i == -1) {
+    DEBUG(0,("Error - all dirptrs in use??\n"));
+    return(-1);
+  }
+
+  if (dirptrs[i].valid)
+    dptr_close(i);
+
+  dirptrs[i].ptr = Connections[cnum].dirptr;
+  string_set(&dirptrs[i].path,path);
+  dirptrs[i].lastused = dircounter++;
+  dirptrs[i].finished = False;
+  dirptrs[i].cnum = cnum;
+  dirptrs[i].pid = pid;
+  dirptrs[i].expect_close = expect_close;
+  dirptrs[i].wcard = NULL; /* Only used in lanman2 searches */
+  dirptrs[i].attr = 0; /* Only used in lanman2 searches */
+  dirptrs[i].valid = True;
+
+  DEBUG(3,("creating new dirptr %d for path %s, expect_close = %d\n",
+          i,path,expect_close));  
+
+  return(i);
+}
+
+#define DPTR_MASK ((uint32)(((uint32)1)<<31))
+
+/****************************************************************************
+fill the 5 byte server reserved dptr field
+****************************************************************************/
+BOOL dptr_fill(char *buf1,unsigned int key)
+{
+  unsigned char *buf = (unsigned char *)buf1;
+  void *p = dptr_get(key,0);
+  uint32 offset;
+  if (!p) {
+    DEBUG(1,("filling null dirptr %d\n",key));
+    return(False);
+  }
+  offset = TellDir(p);
+  DEBUG(6,("fill on key %d dirptr 0x%x now at %d\n",key,p,offset));
+  buf[0] = key;
+  SIVAL(buf,1,offset | DPTR_MASK);
+  return(True);
+}
+
+
+/****************************************************************************
+return True is the offset is at zero
+****************************************************************************/
+BOOL dptr_zero(char *buf)
+{
+  return((IVAL(buf,1)&~DPTR_MASK) == 0);
+}
+
+/****************************************************************************
+fetch the dir ptr and seek it given the 5 byte server field
+****************************************************************************/
+void *dptr_fetch(char *buf,int *num)
+{
+  unsigned int key = *(unsigned char *)buf;
+  void *p = dptr_get(key,dircounter++);
+  uint32 offset;
+  if (!p) {
+    DEBUG(3,("fetched null dirptr %d\n",key));
+    return(NULL);
+  }
+  *num = key;
+  offset = IVAL(buf,1)&~DPTR_MASK;
+  SeekDir(p,offset);
+  DEBUG(3,("fetching dirptr %d for path %s at offset %d\n",
+          key,dptr_path(key),offset));
+  return(p);
+}
+
+/****************************************************************************
+fetch the dir ptr and seek it given the lanman2 parameter block
+****************************************************************************/
+void *dptr_fetch_lanman2(char *params,int dptr_num)
+{
+  void *p = dptr_get(dptr_num,dircounter++);
+  uint32 resume_key = SVAL(params,6);
+  BOOL uses_resume_key = BITSETW(params+10,2);
+  BOOL continue_bit = BITSETW(params+10,3);
+
+  if (!p) {
+    DEBUG(3,("fetched null dirptr %d\n",dptr_num));
+    return(NULL);
+  }
+  if(uses_resume_key && !continue_bit)
+    SeekDir(p,resume_key);
+  DEBUG(3,("fetching dirptr %d for path %s\n",dptr_num,dptr_path(dptr_num)));
+  return(p);
+}
+
+/****************************************************************************
+  get a directory entry
+****************************************************************************/
+BOOL get_dir_entry(int cnum,char *mask,int dirtype,char *fname,int *size,int *mode,time_t *date,BOOL check_descend)
+{
+  char *dname;
+  BOOL found = False;
+  struct stat sbuf;
+  pstring path;
+  pstring pathreal;
+  BOOL isrootdir;
+  pstring filename;
+  BOOL matched;
+
+  *path = *pathreal = *filename = 0;
+
+  isrootdir = (strequal(Connections[cnum].dirpath,"./") ||
+              strequal(Connections[cnum].dirpath,".") ||
+              strequal(Connections[cnum].dirpath,"/"));
+  
+  if (!Connections[cnum].dirptr)
+    return(False);
+  
+  while (!found)
+    {
+      dname = ReadDirName(Connections[cnum].dirptr);
+
+      DEBUG(6,("readdir on dirptr 0x%x now at offset %d\n",
+           Connections[cnum].dirptr,TellDir(Connections[cnum].dirptr)));
+      
+      if (dname == NULL) 
+       return(False);
+      
+      matched = False;
+
+      strcpy(filename,dname);      
+
+      if ((strcmp(filename,mask) == 0) ||
+         (name_map_mangle(filename,True,SNUM(cnum)) &&
+          mask_match(filename,mask,False,False)))
+       {
+         if (isrootdir && (strequal(filename,"..") || strequal(filename,".")))
+           continue;
+
+         strcpy(fname,filename);
+         *path = 0;
+         strcpy(path,Connections[cnum].dirpath);
+         strcat(path,"/");
+         strcpy(pathreal,path);
+         strcat(path,fname);
+         strcat(pathreal,dname);
+         if (sys_stat(pathreal,&sbuf) != 0) 
+           {
+             DEBUG(5,("Couldn't stat 1 [%s]\n",path));
+             continue;
+           }
+
+         if (check_descend &&
+             !strequal(fname,".") && !strequal(fname,".."))
+           continue;
+         
+         *mode = dos_mode(cnum,pathreal,&sbuf);
+
+         if (((*mode & ~dirtype) & (aHIDDEN | aSYSTEM | aDIR)) != 0)
+           {         
+             DEBUG(5,("[%s] attribs didn't match %x\n",filename,dirtype));
+             continue;
+           }
+         *size = sbuf.st_size;
+         *date = sbuf.st_mtime;
+
+         DEBUG(5,("get_dir_entry found %s fname=%s\n",pathreal,fname));
+         
+         found = True;
+       }
+    }
+
+  return(found);
+}
+
+
+
+typedef struct
+{
+  int pos;
+  int numentries;
+  int mallocsize;
+  char *data;
+  char *current;
+} Dir;
+
+
+/*******************************************************************
+open a directory
+********************************************************************/
+void *OpenDir(char *name)
+{
+  Dir *dirp;
+  char *n;
+  void *p = sys_opendir(name);
+  int used=0;
+
+  if (!p) return(NULL);
+  dirp = (Dir *)malloc(sizeof(Dir));
+  if (!dirp) {
+    closedir(p);
+    return(NULL);
+  }
+  dirp->pos = dirp->numentries = dirp->mallocsize = 0;
+  dirp->data = dirp->current = NULL;
+
+  while ((n = readdirname(p))) {
+    int l = strlen(n)+1;
+    if (used + l > dirp->mallocsize) {
+      int s = MAX(used+l,used+2000);
+      char *r;
+      r = (char *)Realloc(dirp->data,s);
+      if (!r) {
+       DEBUG(0,("Out of memory in OpenDir\n"));
+       break;
+      }
+      dirp->data = r;
+      dirp->mallocsize = s;
+      dirp->current = dirp->data;
+    }
+    strcpy(dirp->data+used,n);
+    used += l;
+    dirp->numentries++;
+  }
+
+  closedir(p);
+  return((void *)dirp);
+}
+
+
+/*******************************************************************
+close a directory
+********************************************************************/
+void CloseDir(void *p)
+{
+  Dir *dirp = (Dir *)p;
+  if (!dirp) return;    
+  if (dirp->data) free(dirp->data);
+  free(dirp);
+}
+
+/*******************************************************************
+read from a directory
+********************************************************************/
+char *ReadDirName(void *p)
+{
+  char *ret;
+  Dir *dirp = (Dir *)p;
+
+  if (!dirp || !dirp->current || dirp->pos >= dirp->numentries) return(NULL);
+
+  ret = dirp->current;
+  dirp->current = skip_string(dirp->current,1);
+  dirp->pos++;
+
+  return(ret);
+}
+
+
+/*******************************************************************
+seek a dir
+********************************************************************/
+BOOL SeekDir(void *p,int pos)
+{
+  Dir *dirp = (Dir *)p;
+
+  if (!dirp) return(False);
+
+  if (pos < dirp->pos) {
+    dirp->current = dirp->data;
+    dirp->pos = 0;
+  }
+
+  while (dirp->pos < pos && ReadDirName(p)) ;
+
+  return(dirp->pos == pos);
+}
+
+/*******************************************************************
+tell a dir position
+********************************************************************/
+int TellDir(void *p)
+{
+  Dir *dirp = (Dir *)p;
+
+  if (!dirp) return(-1);
+  
+  return(dirp->pos);
+}
+
+
+static int dir_cache_size = 0;
+static struct dir_cache {
+  struct dir_cache *next;
+  struct dir_cache *prev;
+  char *path;
+  char *name;
+  char *dname;
+  int snum;
+} *dir_cache = NULL;
+
+/*******************************************************************
+add an entry to the directory cache
+********************************************************************/
+void DirCacheAdd(char *path,char *name,char *dname,int snum)
+{
+  struct dir_cache *entry = (struct dir_cache *)malloc(sizeof(*entry));
+  if (!entry) return;
+  entry->path = strdup(path);
+  entry->name = strdup(name);
+  entry->dname = strdup(dname);
+  entry->snum = snum;
+  if (!entry->path || !entry->name || !entry->dname) return;
+
+  entry->next = dir_cache;
+  entry->prev = NULL;
+  if (entry->next) entry->next->prev = entry;
+  dir_cache = entry;
+
+  DEBUG(4,("Added dir cache entry %s %s -> %s\n",path,name,dname));
+  
+  if (dir_cache_size == DIRCACHESIZE) {
+    for (entry=dir_cache; entry->next; entry=entry->next) ;
+    free(entry->path);
+    free(entry->name);
+    free(entry->dname);
+    if (entry->prev) entry->prev->next = entry->next;
+    free(entry);
+  } else {
+    dir_cache_size++;
+  }
+}
+
+
+/*******************************************************************
+check for an entry in the directory cache
+********************************************************************/
+char *DirCacheCheck(char *path,char *name,int snum)
+{
+  struct dir_cache *entry;
+
+  for (entry=dir_cache; entry; entry=entry->next) {
+    if (entry->snum == snum &&
+       strcmp(path,entry->path) == 0 &&
+       strcmp(name,entry->name) == 0) {
+      DEBUG(4,("Got dir cache hit on %s %s -> %s\n",path,name,entry->dname));
+      return(entry->dname);
+    }
+  }
+
+  return(NULL);
+}
+
+/*******************************************************************
+flush entries in the dir_cache
+********************************************************************/
+void DirCacheFlush(int snum)
+{
+  struct dir_cache *entry,*next;
+
+  for (entry=dir_cache; entry; entry=next) {
+    if (entry->snum == snum) {
+      free(entry->path);
+      free(entry->dname);
+      free(entry->name);
+      next = entry->next;
+      if (entry->prev) entry->prev->next = entry->next;
+      if (entry->next) entry->next->prev = entry->prev;
+      if (dir_cache == entry) dir_cache = entry->next; 
+      free(entry);
+    } else {
+      next = entry->next;
+    }
+  }
+}
+
+
+#ifdef REPLACE_GETWD
+/* This is getcwd.c from bash.  It is needed in Interactive UNIX.  To
+ * add support for another OS you need to determine which of the
+ * conditional compilation macros you need to define.  All the options
+ * are defined for Interactive UNIX.
+ */
+#ifdef ISC
+#define HAVE_UNISTD_H
+#define USGr3
+#define USG
+#endif
+
+#if defined (HAVE_UNISTD_H)
+#  include <unistd.h>
+#endif
+
+#if defined (__STDC__)
+#  define CONST const
+#  define PTR void *
+#else /* !__STDC__ */
+#  define CONST
+#  define PTR char *
+#endif /* !__STDC__ */
+
+#if !defined (PATH_MAX)
+#  if defined (MAXPATHLEN)
+#    define PATH_MAX MAXPATHLEN
+#  else /* !MAXPATHLEN */
+#    define PATH_MAX 1024
+#  endif /* !MAXPATHLEN */
+#endif /* !PATH_MAX */
+
+#if defined (_POSIX_VERSION) || defined (USGr3) || defined (HAVE_DIRENT_H)
+#  if !defined (HAVE_DIRENT)
+#    define HAVE_DIRENT
+#  endif /* !HAVE_DIRENT */
+#endif /* _POSIX_VERSION || USGr3 || HAVE_DIRENT_H */
+
+#if defined (HAVE_DIRENT)
+#  define D_NAMLEN(d)  (strlen ((d)->d_name))
+#else
+#  define D_NAMLEN(d)  ((d)->d_namlen)
+#endif /* ! (_POSIX_VERSION || USGr3) */
+
+#if defined (USG) || defined (USGr3)
+#  define d_fileno d_ino
+#endif
+
+#if !defined (alloca)
+extern char *alloca ();
+#endif /* alloca */
+
+/* Get the pathname of the current working directory,
+   and put it in SIZE bytes of BUF.  Returns NULL if the
+   directory couldn't be determined or SIZE was too small.
+   If successful, returns BUF.  In GNU, if BUF is NULL,
+   an array is allocated with `malloc'; the array is SIZE
+   bytes long, unless SIZE <= 0, in which case it is as
+   big as necessary.  */
+#if defined (__STDC__)
+char *
+getcwd (char *buf, size_t size)
+#else /* !__STDC__ */
+char *
+getcwd (buf, size)
+     char *buf;
+     int size;
+#endif /* !__STDC__ */
+{
+  static CONST char dots[]
+    = "../../../../../../../../../../../../../../../../../../../../../../../\
+../../../../../../../../../../../../../../../../../../../../../../../../../../\
+../../../../../../../../../../../../../../../../../../../../../../../../../..";
+  CONST char *dotp, *dotlist;
+  size_t dotsize;
+  dev_t rootdev, thisdev;
+  ino_t rootino, thisino;
+  char path[PATH_MAX + 1];
+  register char *pathp;
+  char *pathbuf;
+  size_t pathsize;
+  struct stat st;
+
+  if (buf != NULL && size == 0)
+    {
+      errno = EINVAL;
+      return ((char *)NULL);
+    }
+
+  pathsize = sizeof (path);
+  pathp = &path[pathsize];
+  *--pathp = '\0';
+  pathbuf = path;
+
+  if (stat (".", &st) < 0)
+    return ((char *)NULL);
+  thisdev = st.st_dev;
+  thisino = st.st_ino;
+
+  if (stat ("/", &st) < 0)
+    return ((char *)NULL);
+  rootdev = st.st_dev;
+  rootino = st.st_ino;
+
+  dotsize = sizeof (dots) - 1;
+  dotp = &dots[sizeof (dots)];
+  dotlist = dots;
+  while (!(thisdev == rootdev && thisino == rootino))
+    {
+      register DIR *dirstream;
+      register struct dirent *d;
+      dev_t dotdev;
+      ino_t dotino;
+      char mount_point;
+      int namlen;
+
+      /* Look at the parent directory.  */
+      if (dotp == dotlist)
+       {
+         /* My, what a deep directory tree you have, Grandma.  */
+         char *new;
+         if (dotlist == dots)
+           {
+             new = malloc (dotsize * 2 + 1);
+             if (new == NULL)
+               goto lose;
+             memcpy (new, dots, dotsize);
+           }
+         else
+           {
+             new = realloc ((PTR) dotlist, dotsize * 2 + 1);
+             if (new == NULL)
+               goto lose;
+           }
+         memcpy (&new[dotsize], new, dotsize);
+         dotp = &new[dotsize];
+         dotsize *= 2;
+         new[dotsize] = '\0';
+         dotlist = new;
+       }
+
+      dotp -= 3;
+
+      /* Figure out if this directory is a mount point.  */
+      if (stat (dotp, &st) < 0)
+       goto lose;
+      dotdev = st.st_dev;
+      dotino = st.st_ino;
+      mount_point = dotdev != thisdev;
+
+      /* Search for the last directory.  */
+      dirstream = opendir(dotp);
+      if (dirstream == NULL)
+       goto lose;
+      while ((d = (struct dirent *)readdir(dirstream)) != NULL)
+       {
+         if (d->d_name[0] == '.' &&
+             (d->d_name[1] == '\0' ||
+               (d->d_name[1] == '.' && d->d_name[2] == '\0')))
+           continue;
+         if (mount_point || d->d_fileno == thisino)
+           {
+             char *name;
+
+             namlen = D_NAMLEN(d);
+             name = (char *)
+               alloca (dotlist + dotsize - dotp + 1 + namlen + 1);
+             memcpy (name, dotp, dotlist + dotsize - dotp);
+             name[dotlist + dotsize - dotp] = '/';
+             memcpy (&name[dotlist + dotsize - dotp + 1],
+                     d->d_name, namlen + 1);
+             if (lstat (name, &st) < 0)
+               {
+                 int save = errno;
+                 closedir(dirstream);
+                 errno = save;
+                 goto lose;
+               }
+             if (st.st_dev == thisdev && st.st_ino == thisino)
+               break;
+           }
+       }
+      if (d == NULL)
+       {
+         int save = errno;
+         closedir(dirstream);
+         errno = save;
+         goto lose;
+       }
+      else
+       {
+         size_t space;
+
+         while ((space = pathp - pathbuf) <= namlen)
+           {
+             char *new;
+
+             if (pathbuf == path)
+               {
+                 new = malloc (pathsize * 2);
+                 if (!new)
+                   goto lose;
+               }
+             else
+               {
+                 new = realloc ((PTR) pathbuf, (pathsize * 2));
+                 if (!new)
+                   goto lose;
+                 pathp = new + space;
+               }
+             (void) memcpy (new + pathsize + space, pathp, pathsize - space);
+             pathp = new + pathsize + space;
+             pathbuf = new;
+             pathsize *= 2;
+           }
+
+         pathp -= namlen;
+         (void) memcpy (pathp, d->d_name, namlen);
+         *--pathp = '/';
+         closedir(dirstream);
+       }
+
+      thisdev = dotdev;
+      thisino = dotino;
+    }
+
+  if (pathp == &path[sizeof(path) - 1])
+    *--pathp = '/';
+
+  if (dotlist != dots)
+    free ((PTR) dotlist);
+
+  {
+    size_t len = pathbuf + pathsize - pathp;
+    if (buf == NULL)
+      {
+       if (len < (size_t) size)
+         len = size;
+       buf = (char *) malloc (len);
+       if (buf == NULL)
+         goto lose2;
+      }
+    else if ((size_t) size < len)
+      {
+       errno = ERANGE;
+       goto lose2;
+      }
+    (void) memcpy((PTR) buf, (PTR) pathp, len);
+  }
+
+  if (pathbuf != path)
+    free (pathbuf);
+
+  return (buf);
+
+ lose:
+  if ((dotlist != dots) && dotlist)
+    {
+      int e = errno;
+      free ((PTR) dotlist);
+      errno = e;
+    }
+
+ lose2:
+  if ((pathbuf != path) && pathbuf)
+    {
+      int e = errno;
+      free ((PTR) pathbuf);
+      errno = e;
+    }
+  return ((char *)NULL);
+}
+#endif
diff --git a/source/smbd/ipc.c b/source/smbd/ipc.c
new file mode 100644 (file)
index 0000000..8852e57
--- /dev/null
@@ -0,0 +1,2779 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   Inter-process communication and named pipe handling
+   Copyright (C) Andrew Tridgell 1992-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.
+   */
+/*
+   This file handles the named pipe and mailslot calls
+   in the SMBtrans protocol
+   */
+
+#include "includes.h"
+#include "loadparm.h"
+#include "pcap.h"
+
+#ifdef CHECK_TYPES
+#undef CHECK_TYPES
+#endif
+#define CHECK_TYPES 0
+
+extern int DEBUGLEVEL;
+extern int maxxmit;
+extern files_struct Files[];
+extern connection_struct Connections[];
+
+extern fstring local_machine;
+
+#define NERR_Success 0
+#define NERR_badpass 86
+#define NERR_notsupported 50
+
+#define NERR_BASE (2100)
+#define NERR_BufTooSmall (NERR_BASE+23)
+#define NERR_JobNotFound (NERR_BASE+51)
+#define NERR_DestNotFound (NERR_BASE+52)
+#define ERROR_INVALID_LEVEL 124
+#define ERROR_MORE_DATA 234
+
+#define REALLOC(ptr,size) Realloc(ptr,MAX((size),4*1024))
+
+#define ACCESS_READ 0x01
+#define ACCESS_WRITE 0x02
+#define ACCESS_CREATE 0x04
+
+#define SHPWLEN 8              /* share password length */
+#define NNLEN 12               /* 8.3 net name length */
+#define SNLEN 15               /* service name length */
+#define QNLEN 12               /* queue name maximum length */
+
+extern int Client;
+
+static int CopyExpanded(int cnum, int snum, char** dst, char* src, int* n)
+{
+  pstring buf;
+  int l;
+
+  if (!src || !dst || !n || !(*dst)) return(0);
+
+  StrnCpy(buf,src,sizeof(buf)/2);
+  string_sub(buf,"%S",lp_servicename(snum));
+  standard_sub(cnum,buf);
+  StrnCpy(*dst,buf,*n);
+  l = strlen(*dst) + 1;
+  (*dst) += l;
+  (*n) -= l;
+  return l;
+}
+
+static int CopyAndAdvance(char** dst, char* src, int* n)
+{
+  int l;
+  if (!src || !dst || !n || !(*dst)) return(0);
+  StrnCpy(*dst,src,*n);
+  l = strlen(*dst) + 1;
+  (*dst) += l;
+  (*n) -= l;
+  return l;
+}
+
+static int StrlenExpanded(int cnum, int snum, char* s)
+{
+  pstring buf;
+  if (!s) return(0);
+  StrnCpy(buf,s,sizeof(buf)/2);
+  string_sub(buf,"%S",lp_servicename(snum));
+  standard_sub(cnum,buf);
+  return strlen(buf) + 1;
+}
+
+static char* Expand(int cnum, int snum, char* s)
+{
+  static pstring buf;
+  if (!s) return(NULL);
+  StrnCpy(buf,s,sizeof(buf)/2);
+  string_sub(buf,"%S",lp_servicename(snum));
+  standard_sub(cnum,buf);
+  return &buf[0];
+}
+
+/*******************************************************************
+  check a API string for validity when we only need to check the prefix
+  ******************************************************************/
+static BOOL prefix_ok(char *str,char *prefix)
+{
+  return(strncmp(str,prefix,strlen(prefix)) == 0);
+}
+
+
+/****************************************************************************
+  send a trans reply
+  ****************************************************************************/
+static void send_trans_reply(char *outbuf,char *data,char *param,uint16 *setup,
+                            int ldata,int lparam,int lsetup)
+{
+  int i;
+  int this_ldata,this_lparam;
+  int tot_data=0,tot_param=0;
+  int align;
+
+  this_lparam = MIN(lparam,maxxmit - (500+lsetup*SIZEOFWORD)); /* hack */
+  this_ldata = MIN(ldata,maxxmit - (500+lsetup*SIZEOFWORD+this_lparam));
+
+  align = (this_lparam%4);
+
+  set_message(outbuf,10+lsetup,align+this_ldata+this_lparam,True);
+  if (this_lparam)
+    memcpy(smb_buf(outbuf),param,this_lparam);
+  if (this_ldata)
+    memcpy(smb_buf(outbuf)+this_lparam+align,data,this_ldata);
+
+  SSVAL(outbuf,smb_vwv0,lparam);
+  SSVAL(outbuf,smb_vwv1,ldata);
+  SSVAL(outbuf,smb_vwv3,this_lparam);
+  SSVAL(outbuf,smb_vwv4,smb_offset(smb_buf(outbuf),outbuf));
+  SSVAL(outbuf,smb_vwv5,0);
+  SSVAL(outbuf,smb_vwv6,this_ldata);
+  SSVAL(outbuf,smb_vwv7,smb_offset(smb_buf(outbuf)+this_lparam+align,outbuf));
+  SSVAL(outbuf,smb_vwv8,0);
+  SSVAL(outbuf,smb_vwv9,lsetup);
+  for (i=0;i<lsetup;i++)
+    SSVAL(outbuf,smb_vwv10+i*SIZEOFWORD,setup[i]);
+
+  show_msg(outbuf);
+  send_smb(Client,outbuf);
+
+  tot_data = this_ldata;
+  tot_param = this_lparam;
+
+  while (tot_data < ldata || tot_param < lparam)
+    {
+      this_lparam = MIN(lparam-tot_param,maxxmit - 500); /* hack */
+      this_ldata = MIN(ldata-tot_data,maxxmit - (500+this_lparam));
+
+      align = (this_lparam%4);
+
+      set_message(outbuf,10,this_ldata+this_lparam+align,False);
+      if (this_lparam)
+       memcpy(smb_buf(outbuf),param+tot_param,this_lparam);
+      if (this_ldata)
+       memcpy(smb_buf(outbuf)+this_lparam+align,data+tot_data,this_ldata);
+
+      SSVAL(outbuf,smb_vwv3,this_lparam);
+      SSVAL(outbuf,smb_vwv4,smb_offset(smb_buf(outbuf),outbuf));
+      SSVAL(outbuf,smb_vwv5,tot_param);
+      SSVAL(outbuf,smb_vwv6,this_ldata);
+      SSVAL(outbuf,smb_vwv7,smb_offset(smb_buf(outbuf)+this_lparam+align,outbuf));
+      SSVAL(outbuf,smb_vwv8,tot_data);
+      SSVAL(outbuf,smb_vwv9,0);
+
+      show_msg(outbuf);
+      send_smb(Client,outbuf);
+
+      tot_data += this_ldata;
+      tot_param += this_lparam;
+    }
+}
+
+
+
+/****************************************************************************
+  get a print queue
+  ****************************************************************************/
+
+struct pack_desc {
+  char* format;            /* formatstring for structure */
+  char* subformat;  /* subformat for structure */
+  char* base;      /* baseaddress of buffer */
+  int buflen;     /* remaining size for fixed part; on init: length of base */
+  int subcount;            /* count of substructures */
+  char* structbuf;  /* pointer into buffer for remaining fixed part */
+  int stringlen;    /* remaining size for variable part */             
+  char* stringbuf;  /* pointer into buffer for remaining variable part */
+  int neededlen;    /* total needed size */
+  int usedlen;     /* total used size (usedlen <= neededlen and usedlen <= buflen) */
+  char* curpos;            /* current position; pointer into format or subformat */
+  int errcode;
+};
+
+static int get_counter(char** p)
+{
+  int i, n;
+  if (!p || !(*p)) return(1);
+  if (!isdigit(**p)) return 1;
+  for (n = 0;;) {
+    i = **p;
+    if (isdigit(i))
+      n = 10 * n + (i - '0');
+    else
+      return n;
+    (*p)++;
+  }
+}
+
+static int getlen(char* p)
+{
+  int n = 0;
+  if (!p) return(0);
+  while (*p) {
+    switch( *p++ ) {
+    case 'W':                  /* word (2 byte) */
+      n += 2;
+      break;
+    case 'N':                  /* count of substructures (word) at end */
+      n += 2;
+      break;
+    case 'D':                  /* double word (4 byte) */
+    case 'z':                  /* offset to zero terminated string (4 byte) */
+    case 'l':                  /* offset to user data (4 byte) */
+      n += 4;
+      break;
+    case 'b':                  /* offset to data (with counter) (4 byte) */
+      n += 4;
+      get_counter(&p);
+      break;
+    case 'B':                  /* byte (with optional counter) */
+      n += get_counter(&p);
+      break;
+    }
+  }
+  return n;
+}
+
+static BOOL init_package(struct pack_desc* p, int count, int subcount)
+{
+  int n = p->buflen;
+  int i;
+
+  if (!p->format || !p->base) return(False);
+
+  i = count * getlen(p->format);
+  if (p->subformat) i += subcount * getlen(p->subformat);
+  p->structbuf = p->base;
+  p->neededlen = 0;
+  p->usedlen = 0;
+  p->subcount = 0;
+  p->curpos = p->format;
+  if (i > n) {
+    i = n = 0;
+    p->errcode = NERR_BufTooSmall;
+  }
+
+  p->errcode = NERR_Success;
+  p->buflen = i;
+  n -= i;
+  p->stringbuf = p->base + i;
+  p->stringlen = n;
+  return(p->errcode == NERR_Success);
+}
+
+#ifdef __STDC__
+static int package(struct pack_desc* p, ...)
+{
+#else
+static int package(va_alist)
+va_dcl
+{
+  struct pack_desc* p;
+#endif
+  va_list args;
+  int needed=0, stringneeded;
+  char* str=NULL;
+  int is_string=0, stringused;
+  int32 temp;
+
+#ifdef __STDC__
+  va_start(args,p);
+#else
+  va_start(args);
+  p = va_arg(args,struct pack_desc *);
+#endif
+
+  if (!*p->curpos) {
+    if (!p->subcount)
+      p->curpos = p->format;
+    else {
+      p->curpos = p->subformat;
+      p->subcount--;
+    }
+  }
+#if CHECK_TYPES
+  str = va_arg(args,char*);
+  if (strncmp(str,p->curpos,strlen(str)) != 0) {
+    DEBUG(2,("type error in package: %s instead of %*s\n",str,
+            strlen(str),p->curpos));
+    va_end(args);
+#if AJT
+    ajt_panic();
+#endif  
+    return 0;
+  }
+#endif
+  stringneeded = -1;
+
+  if (!p->curpos) return(0);
+
+  switch( *p->curpos++ ) {
+  case 'W':                    /* word (2 byte) */
+    needed = 2;
+    temp = va_arg(args,int);
+    if (p->buflen >= needed) SSVAL(p->structbuf,0,temp);
+    break;
+  case 'N':                    /* count of substructures (word) at end */
+    needed = 2;
+    p->subcount = va_arg(args,int);
+    if (p->buflen >= needed) SSVAL(p->structbuf,0,p->subcount);
+    break;
+  case 'D':                    /* double word (4 byte) */
+    needed = 4;
+    temp = va_arg(args,int);
+    if (p->buflen >= needed) SIVAL(p->structbuf,0,temp);
+    break;
+  case 'B':                    /* byte (with optional counter) */
+    needed = get_counter(&p->curpos);
+    {
+      char *s = va_arg(args,char*);
+      if (p->buflen >= needed) StrnCpy(p->structbuf,s?s:"",needed);
+    }
+    break;
+  case 'z':                    /* offset to zero terminated string (4 byte) */
+    str = va_arg(args,char*);
+    stringneeded = (str ? strlen(str)+1 : 0);
+    is_string = 1;
+    break;
+  case 'l':                    /* offset to user data (4 byte) */
+    str = va_arg(args,char*);
+    stringneeded = va_arg(args,int);
+    is_string = 0;
+    break;
+  case 'b':                    /* offset to data (with counter) (4 byte) */
+    str = va_arg(args,char*);
+    stringneeded = get_counter(&p->curpos);
+    is_string = 0;
+    break;
+  }
+  va_end(args);
+  if (stringneeded >= 0) {
+    needed = 4;
+    if (p->buflen >= needed) {
+      stringused = stringneeded;
+      if (stringused > p->stringlen) {
+       stringused = (is_string ? p->stringlen : 0);
+       if (p->errcode == NERR_Success) p->errcode = ERROR_MORE_DATA;
+      }
+      if (!stringused)
+       SIVAL(p->structbuf,0,0);
+      else {
+       SIVAL(p->structbuf,0,PTR_DIFF(p->stringbuf,p->base));
+       memcpy(p->stringbuf,str?str:"",stringused);
+       if (is_string) p->stringbuf[stringused-1] = '\0';
+       p->stringbuf += stringused;
+       p->stringlen -= stringused;
+       p->usedlen += stringused;
+      }
+    }
+    p->neededlen += stringneeded;
+  }
+  p->neededlen += needed;
+  if (p->buflen >= needed) {
+    p->structbuf += needed;
+    p->buflen -= needed;
+    p->usedlen += needed;
+  }
+  else {
+    if (p->errcode == NERR_Success) p->errcode = NERR_BufTooSmall;
+  }
+  return 1;
+}
+
+#if CHECK_TYPES
+#define PACK(desc,t,v) package(desc,t,v,0,0,0,0)
+#define PACKl(desc,t,v,l) package(desc,t,v,l,0,0,0,0)
+#else
+#define PACK(desc,t,v) package(desc,v)
+#define PACKl(desc,t,v,l) package(desc,v,l)
+#endif
+
+static void PACKI(struct pack_desc* desc,char *t,int v)
+{
+  PACK(desc,t,v);
+}
+
+static void PACKS(struct pack_desc* desc,char *t,char *v)
+{
+  PACK(desc,t,v);
+}
+
+static void PackDriverData(struct pack_desc* desc)
+{
+  char drivdata[4+4+32];
+  SIVAL(drivdata,0,sizeof drivdata); /* cb */
+  SIVAL(drivdata,4,1000);      /* lVersion */
+  memset(drivdata+8,0,32);     /* szDeviceName */
+  strcpy(drivdata+8,"NULL");
+  PACKl(desc,"l",drivdata,sizeof drivdata); /* pDriverData */
+}
+
+static int check_printq_info(struct pack_desc* desc,
+                            int uLevel, const char* id1, const char* id2)
+{
+  desc->subformat = NULL;
+  switch( uLevel ) {
+  case 0:
+    desc->format = "B13";
+    break;
+  case 1:
+    desc->format = "B13BWWWzzzzzWW";
+    break;
+  case 2:
+    desc->format = "B13BWWWzzzzzWN";
+    desc->subformat = "WB21BB16B10zWWzDDz";
+    break;
+  case 3:
+    desc->format = "zWWWWzzzzWWzzl";
+    break;
+  case 4:
+    desc->format = "zWWWWzzzzWNzzl";
+    desc->subformat = "WWzWWDDzz";
+    break;
+  case 5:
+    desc->format = "z";
+    break;
+  default: return False;
+  }
+  if (strcmp(desc->format,id1) != 0) return False;
+  if (desc->subformat && strcmp(desc->subformat,id2) != 0) return False;
+  return True;
+}
+
+static void fill_printjob_info(int cnum, int snum, int uLevel,
+                              struct pack_desc* desc,
+                              print_queue_struct* queue, int n)
+{
+  time_t t = queue->time;
+
+  /* the client expects localtime */
+  t += GMT_TO_LOCAL*TimeDiff(t);
+
+  PACKI(desc,"W",((snum%0xFF)<<8) | (queue->job%0xFF)); /* uJobId */
+  if (uLevel == 1) {
+    PACKS(desc,"B21",queue->user); /* szUserName */
+    PACKS(desc,"B","");                /* pad */
+    PACKS(desc,"B16","");      /* szNotifyName */
+    PACKS(desc,"B10","PM_Q_RAW"); /* szDataType */
+    PACKS(desc,"z","");                /* pszParms */
+    PACKI(desc,"W",n+1);               /* uPosition */
+    PACKI(desc,"W",queue->status); /* fsStatus */
+    PACKS(desc,"z","");                /* pszStatus */
+    PACKI(desc,"D",queue->time); /* ulSubmitted */
+    PACKI(desc,"D",queue->size); /* ulSize */
+    PACKS(desc,"z",queue->file); /* pszComment */
+  }
+  if (uLevel == 2 || uLevel == 3) {
+    PACKI(desc,"W",queue->priority);           /* uPriority */
+    PACKS(desc,"z",queue->user); /* pszUserName */
+    PACKI(desc,"W",n+1);               /* uPosition */
+    PACKI(desc,"W",queue->status); /* fsStatus */
+    PACKI(desc,"D",queue->time); /* ulSubmitted */
+    PACKI(desc,"D",queue->size); /* ulSize */
+    PACKS(desc,"z","Samba");   /* pszComment */
+    PACKS(desc,"z",queue->file); /* pszDocument */
+    if (uLevel == 3) {
+      PACKS(desc,"z","");      /* pszNotifyName */
+      PACKS(desc,"z","PM_Q_RAW"); /* pszDataType */
+      PACKS(desc,"z","");      /* pszParms */
+      PACKS(desc,"z","");      /* pszStatus */
+      PACKS(desc,"z",SERVICE(snum)); /* pszQueue */
+      PACKS(desc,"z","lpd");   /* pszQProcName */
+      PACKS(desc,"z","");      /* pszQProcParms */
+      PACKS(desc,"z","NULL"); /* pszDriverName */
+      PackDriverData(desc);    /* pDriverData */
+      PACKS(desc,"z","");      /* pszPrinterName */
+    }
+  }
+}
+
+static void fill_printq_info(int cnum, int snum, int uLevel,
+                            struct pack_desc* desc,
+                            int count, print_queue_struct* queue,
+                            print_status_struct* status)
+{
+  if (uLevel < 3) {
+    PACKS(desc,"B13",SERVICE(snum));
+  } else {
+    PACKS(desc,"z",Expand(cnum,snum,SERVICE(snum)));
+  }
+  if (uLevel == 1 || uLevel == 2) {
+    PACKS(desc,"B","");                /* alignment */
+    PACKI(desc,"W",5);         /* priority */
+    PACKI(desc,"W",0);         /* start time */
+    PACKI(desc,"W",0);         /* until time */
+    PACKS(desc,"z","");                /* pSepFile */
+    PACKS(desc,"z","lpd");     /* pPrProc */
+    PACKS(desc,"z",SERVICE(snum)); /* pDestinations */
+    PACKS(desc,"z","");                /* pParms */
+    if (snum < 0) {
+      PACKS(desc,"z","UNKNOWN PRINTER");
+      PACKI(desc,"W",LPSTAT_ERROR);
+    }
+    else if (!status || !status->message[0]) {
+      PACKS(desc,"z",Expand(cnum,snum,lp_comment(snum)));
+      PACKI(desc,"W",LPSTAT_OK); /* status */
+    } else {
+      PACKS(desc,"z",status->message);
+      PACKI(desc,"W",status->status); /* status */
+    }
+    PACKI(desc,(uLevel == 1 ? "W" : "N"),count);
+  }
+  if (uLevel == 3 || uLevel == 4) {
+    PACKI(desc,"W",5);         /* uPriority */
+    PACKI(desc,"W",0);         /* uStarttime */
+    PACKI(desc,"W",0);         /* uUntiltime */
+    PACKI(desc,"W",5);         /* pad1 */
+    PACKS(desc,"z","");                /* pszSepFile */
+    PACKS(desc,"z","lpd");     /* pszPrProc */
+    PACKS(desc,"z","");                /* pszParms */
+    if (!status || !status->message[0]) {
+      PACKS(desc,"z",Expand(cnum,snum,lp_comment(snum))); /* pszComment */
+      PACKI(desc,"W",LPSTAT_OK); /* fsStatus */
+    } else {
+      PACKS(desc,"z",status->message); /* pszComment */
+      PACKI(desc,"W",status->status); /* fsStatus */
+    }
+    PACKI(desc,(uLevel == 3 ? "W" : "N"),count);       /* cJobs */
+    PACKS(desc,"z",SERVICE(snum)); /* pszPrinters */
+    PACKS(desc,"z","NULL");    /* pszDriverName */
+    PackDriverData(desc);      /* pDriverData */
+  }
+  if (uLevel == 2 || uLevel == 4) {
+    int i;
+    for (i=0;i<count;i++)
+      fill_printjob_info(cnum,snum,uLevel == 2 ? 1 : 2,desc,&queue[i],i);
+  }
+  DEBUG(3,("fill_printq_info on <%s> gave %d entries\n",SERVICE(snum),count));
+}
+
+static BOOL api_DosPrintQGetInfo(int cnum,int uid, char *param,char *data,
+                                int mdrcnt,int mprcnt,
+                                char **rdata,char **rparam,
+                                int *rdata_len,int *rparam_len)
+{
+  char *str1 = param+2;
+  char *str2 = skip_string(str1,1);
+  char *p = skip_string(str2,1);
+  char *QueueName = p;
+  int uLevel,cbBuf;
+  int count=0;
+  int snum;
+  char* str3;
+  struct pack_desc desc;
+  print_queue_struct *queue=NULL;
+  print_status_struct status;
+  
+  bzero(&status,sizeof(status));
+  bzero(&desc,sizeof(desc));
+  p = skip_string(p,1);
+  uLevel = SVAL(p,0);
+  cbBuf = SVAL(p,2);
+  str3 = p + 4;
+  if ((p = strchr(QueueName,'%'))) *p = 0;
+  DEBUG(3,("PrintQueue uLevel=%d name=%s\n",uLevel,QueueName));
+  /* check it's a supported varient */
+  if (!prefix_ok(str1,"zWrLh")) return False;
+  if (!check_printq_info(&desc,uLevel,str2,str3)) return False;
+  snum = lp_servicenumber(QueueName);
+  if (snum < 0 && pcap_printername_ok(QueueName,NULL)) {
+    int pnum = lp_servicenumber(PRINTERS_NAME);
+    if (pnum >= 0) {
+      lp_add_printer(QueueName,pnum);
+      snum = lp_servicenumber(QueueName);
+    }
+  }
+  
+  if (snum < 0 || !VALID_SNUM(snum)) return(False);
+
+  count = get_printqueue(snum,cnum,&queue,&status);
+  if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+  desc.base = *rdata;
+  desc.buflen = mdrcnt;
+  if (init_package(&desc,1,count)) {
+    desc.subcount = count;
+    fill_printq_info(cnum,snum,uLevel,&desc,count,queue,&status);
+  }
+
+  *rdata_len = desc.usedlen;
+  
+  *rparam_len = 6;
+  *rparam = REALLOC(*rparam,*rparam_len);
+  SSVALS(*rparam,0,desc.errcode);
+  SSVAL(*rparam,2,0);
+  SSVAL(*rparam,4,desc.neededlen);
+  
+  DEBUG(4,("printqgetinfo: errorcode %d\n",desc.errcode));
+
+  if (queue) free(queue);
+  
+  return(True);
+}
+
+
+/****************************************************************************
+  view list of all print jobs on all queues
+  ****************************************************************************/
+static BOOL api_DosPrintQEnum(int cnum, int uid, char* param, char* data,
+                             int mdrcnt, int mprcnt,
+                             char **rdata, char** rparam,
+                             int *rdata_len, int *rparam_len)
+{
+  char *param_format = param+2;
+  char *output_format1 = skip_string(param_format,1);
+  char *p = skip_string(output_format1,1);
+  int uLevel = SVAL(p,0);
+  char *output_format2 = p + 4;
+  int services = lp_numservices();
+  int i, n;
+  struct pack_desc desc;
+  print_queue_struct **queue = NULL;
+  print_status_struct *status = NULL;
+  int* subcntarr = NULL;
+  int queuecnt, subcnt=0, succnt=0;
+  bzero(&desc,sizeof(desc));
+
+  DEBUG(3,("DosPrintQEnum uLevel=%d\n",uLevel));
+  if (prefix_ok(param_format,"WrLeh")) return False;
+  if (!check_printq_info(&desc,uLevel,output_format1,output_format2))
+    return False;
+  queuecnt = 0;
+  for (i = 0; i < services; i++)
+    if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i))
+      queuecnt++;
+  if (uLevel > 0) {
+    queue = (print_queue_struct**)malloc(queuecnt*sizeof(print_queue_struct*));
+    memset(queue,0,queuecnt*sizeof(print_queue_struct*));
+    status = (print_status_struct*)malloc(queuecnt*sizeof(print_status_struct));
+    memset(status,0,queuecnt*sizeof(print_status_struct));
+    subcntarr = (int*)malloc(queuecnt*sizeof(int));
+    subcnt = 0;
+    n = 0;
+    for (i = 0; i < services; i++)
+      if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i)) {
+       subcntarr[n] = get_printqueue(i,cnum,&queue[n],&status[n]);
+       subcnt += subcntarr[n];
+       n++;
+      }
+  }
+  if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+  desc.base = *rdata;
+  desc.buflen = mdrcnt;
+
+  if (init_package(&desc,queuecnt,subcnt)) {
+    n = 0;
+    succnt = 0;
+    for (i = 0; i < services; i++)
+      if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i)) {
+       fill_printq_info(cnum,i,uLevel,&desc,subcntarr[n],queue[n],&status[n]);
+       n++;
+       if (desc.errcode == NERR_Success) succnt = n;
+      }
+  }
+
+  if (subcntarr) free(subcntarr);
+  *rdata_len = desc.usedlen;
+  *rparam_len = 8;
+  *rparam = REALLOC(*rparam,*rparam_len);
+  SSVALS(*rparam,0,desc.errcode);
+  SSVAL(*rparam,2,0);
+  SSVAL(*rparam,4,succnt);
+  SSVAL(*rparam,6,queuecnt);
+  
+  for (i = 0; i < queuecnt; i++) {
+    if (queue && queue[i]) free(queue[i]);
+  }
+
+  if (queue) free(queue);
+  if (status) free(status);
+  
+  return True;
+}
+
+/****************************************************************************
+  get info level for a server list query
+  ****************************************************************************/
+static BOOL check_server_info(int uLevel, char* id)
+{
+  switch( uLevel ) {
+  case 0:
+    if (strcmp(id,"B16") != 0) return False;
+    break;
+  case 1:
+    if (strcmp(id,"B16BBDz") != 0) return False;
+    break;
+  default: 
+    return False;
+  }
+  return True;
+}
+
+/* used for server information: client, nameserv and ipc */
+struct srv_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 */
+};
+
+/*******************************************************************
+  filter out unwanted server info 
+  ******************************************************************/
+static BOOL filter_server_info(struct srv_info_struct *server, 
+                              char *domain)
+{
+  if (*domain)
+    return(strequal(domain, server->domain));
+  
+  return (True); /* be indiscriminate: get all servers! */
+}
+
+/*******************************************************************
+  find server in the files saved by nmbd. Return True if we find it.
+  ******************************************************************/
+static BOOL find_server(struct srv_info_struct *servers, int num_servers,
+                char *domain, char *name)
+{
+  int count;
+
+  if (!servers || num_servers == 0) return (False);
+
+  for (count = 0; count < num_servers; count++)        {
+    struct srv_info_struct *s;
+
+    s = &servers[count];
+
+    if (strequal(name, s->name)) {
+      StrnCpy(domain, s->domain, sizeof(pstring)-1);
+      return (True);
+    }
+  }
+  return (False);
+}
+
+
+/*******************************************************************
+  get server info lists from the files saved by nmbd. Return the
+  number of entries
+  ******************************************************************/
+static int get_server_info(uint32 servertype, 
+                          struct srv_info_struct **servers)
+{
+  FILE *f;
+  pstring fname;
+  int count=0;
+  int alloced=0;
+  pstring line;
+
+  strcpy(fname,lp_lockdir());
+  trim_string(fname,NULL,"/");
+  strcat(fname,"/");
+  strcat(fname,SERVER_LIST);
+
+  f = fopen(fname,"r");
+
+  if (!f) {
+    DEBUG(4,("Can't open %s - %s\n",fname,strerror(errno)));
+    return(0);
+  }
+  if (servertype == SV_TYPE_ALL) servertype &= ~SV_TYPE_DOMAIN_ENUM;
+
+  while (!feof(f))
+  {
+    fstring stype;
+    struct srv_info_struct *s;
+    char *ptr = line;
+    *ptr = 0;
+
+    fgets(line,sizeof(line)-1,f);
+    if (!*line) continue;
+
+    if (count == alloced) {
+      alloced += 10;
+      (*servers) = (struct srv_info_struct *)
+       Realloc(*servers,sizeof(**servers)*alloced);
+      if (!(*servers)) return(0);
+      bzero((char *)((*servers)+count),sizeof(**servers)*(alloced-count));
+    }
+    s = &(*servers)[count];
+
+    s->server_added = True;
+
+    if (!next_token(&ptr,s->name   , NULL)) continue;
+    if (!next_token(&ptr,stype     , NULL)) continue;
+    if (!next_token(&ptr,s->comment, NULL)) continue;
+    if (!next_token(&ptr,s->domain , NULL)) {
+      /* this allows us to cop with an old nmbd */
+      strcpy(s->domain,my_workgroup()); 
+    }
+
+    if (sscanf(stype,"%X",&s->type) != 1) continue;
+
+    /* doesn't match up: don't want it */
+    if (!(servertype & s->type)) continue;
+
+    /* server entry is a domain, we haven't asked for domains: don't want it */
+    if ((s->type&SV_TYPE_DOMAIN_ENUM) && !(servertype&SV_TYPE_DOMAIN_ENUM))
+      continue;
+
+    DEBUG(4,("Server %20s %8x %25s %15s\n",
+            s->name, stype, s->comment, s->domain));
+
+    count++;
+  }
+
+  fclose(f);
+  return(count);
+}
+
+/*******************************************************************
+  fill in a server info structure
+  ******************************************************************/
+static int fill_srv_info(struct srv_info_struct *service, 
+                        int uLevel, char **buf, int *buflen, 
+                        char **stringbuf, int *stringspace, char *baseaddr)
+{
+  int struct_len;
+  char* p;
+  char* p2;
+  int l2;
+  int len;
+  switch (uLevel) {
+  case 0: struct_len = 16; break;
+  case 1: struct_len = 26; break;
+  default: return -1;
+  }  
+  if (!buf)
+    {
+      len = 0;
+      switch (uLevel) 
+       {
+       case 1:
+         len = strlen(service->comment)+1;
+         break;
+       }
+
+      if (buflen) *buflen = struct_len;
+      if (stringspace) *stringspace = len;
+      return struct_len + len;
+    }
+  
+  len = struct_len;
+  p = *buf;
+  if (*buflen < struct_len) return -1;
+  if (stringbuf)
+    {
+      p2 = *stringbuf;
+      l2 = *stringspace;
+    }
+  else
+    {
+      p2 = p + struct_len;
+      l2 = *buflen - struct_len;
+    }
+  if (!baseaddr) baseaddr = p;
+  
+  switch (uLevel)
+    {
+    case 0:
+      StrnCpy(p,service->name,15);
+      break;
+
+    case 1:
+      StrnCpy(p,service->name,15);
+      SIVAL(p,18,service->type);
+      SIVAL(p,22,PTR_DIFF(p2,baseaddr));
+      len += CopyAndAdvance(&p2,service->comment,&l2);
+      break;
+    }
+
+  if (stringbuf)
+    {
+      *buf = p + struct_len;
+      *buflen -= struct_len;
+      *stringbuf = p2;
+      *stringspace = l2;
+    }
+  else
+    {
+      *buf = p2;
+      *buflen -= len;
+    }
+  return len;
+}
+
+
+/****************************************************************************
+  view list of servers available (or possibly domains). The info is
+  extracted from lists saved by nmbd on the local host
+  ****************************************************************************/
+static BOOL api_RNetServerEnum(int cnum, int uid, char *param, char *data,
+                              int mdrcnt, int mprcnt, char **rdata, 
+                              char **rparam, int *rdata_len, int *rparam_len)
+{
+  char *str1 = param+2;
+  char *str2 = skip_string(str1,1);
+  char *p = skip_string(str2,1);
+  int uLevel = SVAL(p,0);
+  int buf_len = SVAL(p,2);
+  uint32 servertype = IVAL(p,4);
+  char *p2;
+  int data_len, fixed_len, string_len;
+  int f_len, s_len;
+  struct srv_info_struct *servers=NULL;
+  int counted=0,total=0;
+  int i;
+  fstring domain;
+  BOOL domain_request = (servertype & SV_TYPE_DOMAIN_ENUM) &&
+                       !(servertype == SV_TYPE_ALL);
+
+  domain[0] = 0;
+  p += 8;
+
+  if (!prefix_ok(str1,"WrLehD")) return False;
+  if (!check_server_info(uLevel,str2)) return False;
+  
+  DEBUG(4, ("server request level: %s\n", str2));
+
+  if (strcmp(str1, "WrLehDO") == 0)
+  {
+    /* asking for servers. we will have to work out which workgroup was
+       requested, as we maintain lists for multiple workgroups */
+  }
+  else if (strcmp(str1, "WrLehDz") == 0)
+  {
+    /* asking for a specific workgroup */
+    StrnCpy(domain, p, sizeof(fstring)-1);
+  }
+
+  if (lp_browse_list())
+  {
+    total = get_server_info(servertype,&servers);
+  }
+
+  if (!domain[0] && !domain_request) {
+    extern fstring remote_machine;
+    /* must be a server request with an assumed domain. find a domain */
+    
+    if (find_server(servers, total, domain, remote_machine)) {
+      DEBUG(4, ("No domain specified: using %s for %s\n",
+               domain, remote_machine));
+    } else {
+      /* default to soemthing sensible */
+      strcpy(domain,my_workgroup());
+    }
+  }
+
+  data_len = fixed_len = string_len = 0;
+
+  for (i=0;i<total;i++)
+    if (filter_server_info(&servers[i],domain)) {
+      data_len += fill_srv_info(&servers[i],uLevel,0,&f_len,0,&s_len,0);
+      if (data_len <= buf_len)
+       {
+         counted++;
+         fixed_len += f_len;
+         string_len += s_len;
+       }
+    }
+
+  *rdata_len = fixed_len + string_len;
+  *rdata = REALLOC(*rdata,*rdata_len);
+  bzero(*rdata,*rdata_len);
+  
+  p2 = (*rdata) + fixed_len;   /* auxilliary data (strings) will go here */
+  p = *rdata;
+  f_len = fixed_len;
+  s_len = string_len;
+
+  {
+    int count2 = counted;
+    for (i = 0; i < total && count2;i++) {
+      if (filter_server_info(&servers[i],domain)) {
+       fill_srv_info(&servers[i],uLevel,&p,&f_len,&p2,&s_len,*rdata);
+       count2--;
+      }
+    }
+  }
+  
+  *rparam_len = 8;
+  *rparam = REALLOC(*rparam,*rparam_len);
+  SSVAL(*rparam,0,NERR_Success);
+  SSVAL(*rparam,2,0);
+  SSVAL(*rparam,4,counted);
+  SSVAL(*rparam,6,total);
+
+  if (servers) free(servers);
+
+  DEBUG(3,("NetServerEnum domain = %s uLevel=%d counted=%d total=%d\n",
+          domain,uLevel,counted,total));
+
+  return(True);
+}
+
+
+/****************************************************************************
+  get info about a share
+  ****************************************************************************/
+static BOOL check_share_info(int uLevel, char* id)
+{
+  switch( uLevel ) {
+  case 0:
+    if (strcmp(id,"B13") != 0) return False;
+    break;
+  case 1:
+    if (strcmp(id,"B13BWz") != 0) return False;
+    break;
+  case 2:
+    if (strcmp(id,"B13BWzWWWzB9B") != 0) return False;
+    break;
+  case 91:
+    if (strcmp(id,"B13BWzWWWzB9BB9BWzWWzWW") != 0) return False;
+    break;
+  default: return False;
+  }
+  return True;
+}
+
+static int fill_share_info(int cnum, int snum, int uLevel,
+                          char** buf, int* buflen,
+                          char** stringbuf, int* stringspace, char* baseaddr)
+{
+  int struct_len;
+  char* p;
+  char* p2;
+  int l2;
+  int len;
+  switch( uLevel ) {
+  case 0: struct_len = 13; break;
+  case 1: struct_len = 20; break;
+  case 2: struct_len = 40; break;
+  case 91: struct_len = 68; break;
+  default: return -1;
+  }
+  
+  if (!buf)
+    {
+      len = 0;
+      if (uLevel > 0) len += StrlenExpanded(cnum,snum,lp_comment(snum));
+      if (uLevel > 1) len += strlen(lp_pathname(snum)) + 1;
+      if (buflen) *buflen = struct_len;
+      if (stringspace) *stringspace = len;
+      return struct_len + len;
+    }
+  
+  len = struct_len;
+  p = *buf;
+  if ((*buflen) < struct_len) return -1;
+  if (stringbuf)
+    {
+      p2 = *stringbuf;
+      l2 = *stringspace;
+    }
+  else
+    {
+      p2 = p + struct_len;
+      l2 = (*buflen) - struct_len;
+    }
+  if (!baseaddr) baseaddr = p;
+  
+  StrnCpy(p,lp_servicename(snum),13);
+  
+  if (uLevel > 0)
+    {
+      int type;
+      CVAL(p,13) = 0;
+      type = STYPE_DISKTREE;
+      if (lp_print_ok(snum)) type = STYPE_PRINTQ;
+      if (strequal("IPC$",lp_servicename(snum))) type = STYPE_IPC;
+      SSVAL(p,14,type);                /* device type */
+      SIVAL(p,16,PTR_DIFF(p2,baseaddr));
+      len += CopyExpanded(cnum,snum,&p2,lp_comment(snum),&l2);
+    }
+  
+  if (uLevel > 1)
+    {
+      SSVAL(p,20,ACCESS_READ|ACCESS_WRITE|ACCESS_CREATE); /* permissions */
+      SSVALS(p,22,-1);         /* max uses */
+      SSVAL(p,24,1); /* current uses */
+      SIVAL(p,26,PTR_DIFF(p2,baseaddr)); /* local pathname */
+      len += CopyAndAdvance(&p2,lp_pathname(snum),&l2);
+      memset(p+30,0,SHPWLEN+2); /* passwd (reserved), pad field */
+    }
+  
+  if (uLevel > 2)
+    {
+      memset(p+40,0,SHPWLEN+2);
+      SSVAL(p,50,0);
+      SIVAL(p,52,0);
+      SSVAL(p,56,0);
+      SSVAL(p,58,0);
+      SIVAL(p,60,0);
+      SSVAL(p,64,0);
+      SSVAL(p,66,0);
+    }
+       
+  if (stringbuf)
+    {
+      (*buf) = p + struct_len;
+      (*buflen) -= struct_len;
+      (*stringbuf) = p2;
+      (*stringspace) = l2;
+    }
+  else
+    {
+      (*buf) = p2;
+      (*buflen) -= len;
+    }
+  return len;
+}
+
+static BOOL api_RNetShareGetInfo(int cnum,int uid, char *param,char *data,
+                                int mdrcnt,int mprcnt,
+                                char **rdata,char **rparam,
+                                int *rdata_len,int *rparam_len)
+{
+  char *str1 = param+2;
+  char *str2 = skip_string(str1,1);
+  char *netname = skip_string(str2,1);
+  char *p = skip_string(netname,1);
+  int uLevel = SVAL(p,0);
+  int snum = find_service(netname);
+  
+  if (snum < 0) return False;
+  
+  /* check it's a supported varient */
+  if (!prefix_ok(str1,"zWrLh")) return False;
+  if (!check_share_info(uLevel,str2)) return False;
+  *rdata = REALLOC(*rdata,mdrcnt);
+  p = *rdata;
+  *rdata_len = fill_share_info(cnum,snum,uLevel,&p,&mdrcnt,0,0,0);
+  if (*rdata_len < 0) return False;
+  *rparam_len = 6;
+  *rparam = REALLOC(*rparam,*rparam_len);
+  SSVAL(*rparam,0,NERR_Success);
+  SSVAL(*rparam,2,0);          /* converter word */
+  SSVAL(*rparam,4,*rdata_len);
+  return(True);
+}
+
+/****************************************************************************
+  view list of shares available
+  ****************************************************************************/
+static BOOL api_RNetShareEnum(int cnum,int uid, char *param,char *data,
+                             int mdrcnt,int mprcnt,
+                             char **rdata,char **rparam,
+                             int *rdata_len,int *rparam_len)
+{
+  char *str1 = param+2;
+  char *str2 = skip_string(str1,1);
+  char *p = skip_string(str2,1);
+  int uLevel = SVAL(p,0);
+  int buf_len = SVAL(p,2);
+  char *p2;
+  int count=lp_numservices();
+  int total=0,counted=0;
+  int i;
+  int data_len, fixed_len, string_len;
+  int f_len, s_len;
+  if (!prefix_ok(str1,"WrLeh")) return False;
+  if (!check_share_info(uLevel,str2)) return False;
+  
+  data_len = fixed_len = string_len = 0;
+  for (i=0;i<count;i++)
+    if (lp_browseable(i) && lp_snum_ok(i))
+      {
+       total++;
+       data_len += fill_share_info(cnum,i,uLevel,0,&f_len,0,&s_len,0);
+       if (data_len <= buf_len)
+         {
+           counted++;
+           fixed_len += f_len;
+           string_len += s_len;
+         }
+      }
+  *rdata_len = fixed_len + string_len;
+  *rdata = REALLOC(*rdata,*rdata_len);
+  memset(*rdata,0,*rdata_len);
+  
+  p2 = (*rdata) + fixed_len;   /* auxillery data (strings) will go here */
+  p = *rdata;
+  f_len = fixed_len;
+  s_len = string_len;
+  for (i = 0; i < count;i++)
+    if (lp_browseable(i) && lp_snum_ok(i))
+      if (fill_share_info(cnum,i,uLevel,&p,&f_len,&p2,&s_len,*rdata) < 0)
+       break;
+  
+  *rparam_len = 8;
+  *rparam = REALLOC(*rparam,*rparam_len);
+  SSVAL(*rparam,0,NERR_Success);
+  SSVAL(*rparam,2,0);
+  SSVAL(*rparam,4,counted);
+  SSVAL(*rparam,6,total);
+  
+  DEBUG(3,("RNetShareEnum gave %d entries of %d (%d %d %d %d)\n",
+          counted,total,uLevel,
+          buf_len,*rdata_len,mdrcnt));
+  return(True);
+}
+
+
+
+/****************************************************************************
+  get the time of day info
+  ****************************************************************************/
+static BOOL api_NetRemoteTOD(int cnum,int uid, char *param,char *data,
+                            int mdrcnt,int mprcnt,
+                            char **rdata,char **rparam,
+                            int *rdata_len,int *rparam_len)
+{
+  char *p;
+  *rparam_len = 4;
+  *rparam = REALLOC(*rparam,*rparam_len);
+
+  *rdata_len = 21;
+  *rdata = REALLOC(*rdata,*rdata_len);
+
+  SSVAL(*rparam,0,NERR_Success);
+  SSVAL(*rparam,2,0);          /* converter word */
+
+  p = *rdata;
+
+  {
+    struct tm *t;
+    time_t unixdate = time(NULL);
+
+    put_dos_date3(p,0,unixdate); /* this is the time that is looked at
+                                   by NT in a "net time" operation,
+                                   it seems to ignore the one below */
+
+    /* the client expects to get localtime, not GMT, in this bit 
+       (I think, this needs testing) */
+    t = LocalTime(&unixdate,GMT_TO_LOCAL);
+
+    SIVAL(p,4,0);              /* msecs ? */
+    CVAL(p,8) = t->tm_hour;
+    CVAL(p,9) = t->tm_min;
+    CVAL(p,10) = t->tm_sec;
+    CVAL(p,11) = 0;            /* hundredths of seconds */
+    SSVALS(p,12,TimeDiff(unixdate)/60); /* timezone in minutes from GMT */
+    SSVAL(p,14,10000);         /* timer interval in 0.0001 of sec */
+    CVAL(p,16) = t->tm_mday;
+    CVAL(p,17) = t->tm_mon + 1;
+    SSVAL(p,18,1900+t->tm_year);
+    CVAL(p,20) = t->tm_wday;
+  }
+
+
+  return(True);
+}
+
+/****************************************************************************
+  set the user password
+  ****************************************************************************/
+static BOOL api_SetUserPassword(int cnum,int uid, char *param,char *data,
+                               int mdrcnt,int mprcnt,
+                               char **rdata,char **rparam,
+                               int *rdata_len,int *rparam_len)
+{
+  char *p = skip_string(param+2,2);
+  fstring user;
+  fstring pass1,pass2;
+
+  strcpy(user,p);
+
+  p = skip_string(p,1);
+
+  StrnCpy(pass1,p,16);
+  StrnCpy(pass2,p+16,16);
+
+  *rparam_len = 4;
+  *rparam = REALLOC(*rparam,*rparam_len);
+
+  *rdata_len = 0;
+
+  SSVAL(*rparam,0,NERR_Success);
+  SSVAL(*rparam,2,0);          /* converter word */
+
+  DEBUG(3,("Set password for <%s>\n",user));
+
+  if (!password_ok(user,pass1,strlen(pass1),NULL,False) || 
+      !chgpasswd(user,pass1,pass2))
+    SSVAL(*rparam,0,NERR_badpass);
+
+  bzero(pass1,sizeof(fstring));
+  bzero(pass2,sizeof(fstring));         
+        
+  return(True);
+}
+
+/****************************************************************************
+  delete a print job
+  Form: <W> <> 
+  ****************************************************************************/
+static BOOL api_RDosPrintJobDel(int cnum,int uid, char *param,char *data,
+                               int mdrcnt,int mprcnt,
+                               char **rdata,char **rparam,
+                               int *rdata_len,int *rparam_len)
+{
+  int function = SVAL(param,0);
+  char *str1 = param+2;
+  char *str2 = skip_string(str1,1);
+  char *p = skip_string(str2,1);
+  int jobid = (SVAL(p,0)&0xFF); /* the snum and jobid are encoded
+                                  by the print queue api */
+  int snum = (SVAL(p,0)>>8);  
+  int i, count;
+
+
+  /* check it's a supported varient */
+  if (!(strcsequal(str1,"W") && strcsequal(str2,"")))
+    return(False);
+
+  *rparam_len = 4;
+  *rparam = REALLOC(*rparam,*rparam_len);
+
+  *rdata_len = 0;
+
+  SSVAL(*rparam,0,NERR_Success);
+
+  if (snum >= 0 && VALID_SNUM(snum))
+    {
+      print_queue_struct *queue=NULL;
+      lpq_reset(snum);
+      count = get_printqueue(snum,cnum,&queue,NULL);
+  
+      for (i=0;i<count;i++)
+       if ((queue[i].job%0xFF) == jobid)
+         {
+           switch (function) {
+           case 81:            /* delete */ 
+             DEBUG(3,("Deleting queue entry %d\n",queue[i].job));
+             del_printqueue(cnum,snum,queue[i].job);
+             break;
+           case 82:            /* pause */
+           case 83:            /* resume */
+             DEBUG(3,("%s queue entry %d\n",
+                      (function==82?"pausing":"resuming"),queue[i].job));
+             status_printjob(cnum,snum,queue[i].job,
+                             (function==82?LPQ_PAUSED:LPQ_QUEUED));
+             break;
+           }
+           break;
+         }
+  
+      if (i==count)
+       SSVAL(*rparam,0,NERR_JobNotFound);
+
+      if (queue) free(queue);
+    }
+
+  SSVAL(*rparam,2,0);          /* converter word */
+
+  return(True);
+}
+
+static BOOL api_WPrintQueuePurge(int cnum,int uid, char *param,char *data,
+                                int mdrcnt,int mprcnt,
+                                char **rdata,char **rparam,
+                                int *rdata_len,int *rparam_len)
+{
+  char *str1 = param+2;
+  char *str2 = skip_string(str1,1);
+  char *QueueName = skip_string(str2,1);
+  int snum;
+
+  /* check it's a supported varient */
+  if (!(strcsequal(str1,"z") && strcsequal(str2,"")))
+    return(False);
+
+  *rparam_len = 4;
+  *rparam = REALLOC(*rparam,*rparam_len);
+
+  *rdata_len = 0;
+
+  SSVAL(*rparam,0,NERR_Success);
+  SSVAL(*rparam,2,0);          /* converter word */
+
+  snum = lp_servicenumber(QueueName);
+  if (snum < 0 && pcap_printername_ok(QueueName,NULL)) {
+    int pnum = lp_servicenumber(PRINTERS_NAME);
+    if (pnum >= 0) {
+      lp_add_printer(QueueName,pnum);
+      snum = lp_servicenumber(QueueName);
+    }
+  }
+
+  if (snum >= 0 && VALID_SNUM(snum)) {
+    print_queue_struct *queue=NULL;
+    int i, count;
+    lpq_reset(snum);
+    
+    count = get_printqueue(snum,cnum,&queue,NULL);
+    for (i = 0; i < count; i++)
+      del_printqueue(cnum,snum,queue[i].job);
+    
+    if (queue) free(queue);
+  }
+
+  DEBUG(3,("Print queue purge, queue=%s\n",QueueName));
+
+  return(True);
+}
+
+
+/****************************************************************************
+  set the property of a print job (undocumented?)
+  ? function = 0xb -> set name of print job
+  ? function = 0x6 -> move print job up/down
+  Form: <WWsTP> <WWzWWDDzzzzzzzzzzlz> 
+  or   <WWsTP> <WB21BB16B10zWWzDDz> 
+****************************************************************************/
+static int check_printjob_info(struct pack_desc* desc,
+                              int uLevel, char* id)
+{
+  desc->subformat = NULL;
+  switch( uLevel ) {
+  case 0: desc->format = "W"; break;
+  case 1: desc->format = "WB21BB16B10zWWzDDz"; break;
+  case 2: desc->format = "WWzWWDDzz"; break;
+  case 3: desc->format = "WWzWWDDzzzzzzzzzzlz"; break;
+  default: return False;
+  }
+  if (strcmp(desc->format,id) != 0) return False;
+  return True;
+}
+
+static BOOL api_PrintJobInfo(int cnum,int uid,char *param,char *data,
+                            int mdrcnt,int mprcnt,
+                            char **rdata,char **rparam,
+                            int *rdata_len,int *rparam_len)
+{
+  struct pack_desc desc;
+  char *str1 = param+2;
+  char *str2 = skip_string(str1,1);
+  char *p = skip_string(str2,1);
+  int jobid = (SVAL(p,0)&0xFF); /* the snum and jobid are encoded
+                                  by the print queue api */
+  int snum = (SVAL(p,0)>>8);
+  int uLevel = SVAL(p,2);
+  int function = SVAL(p,4);    /* what is this ?? */
+  int i;
+  char *s = data;
+   
+  *rparam_len = 4;
+  *rparam = REALLOC(*rparam,*rparam_len);
+  
+  *rdata_len = 0;
+  
+  /* check it's a supported varient */
+  if ((strcmp(str1,"WWsTP")) || (!check_printjob_info(&desc,uLevel,str2)))
+    return(False);
+   
+  switch (function) {
+  case 0x6:    /* change job place in the queue, data gives the new place */
+    if (snum >= 0 && VALID_SNUM(snum))
+      {
+       print_queue_struct *queue=NULL;
+       int count;
+  
+       lpq_reset(snum);
+       count = get_printqueue(snum,cnum,&queue,NULL);
+       for (i=0;i<count;i++)   /* find job */
+         if ((queue[i].job%0xFF) == jobid) break;
+           
+       if (i==count) {
+         desc.errcode=NERR_JobNotFound;
+         if (queue) free(queue);
+       }
+       else {
+         desc.errcode=NERR_Success;
+         i++;
+#if 0  
+         {
+           int place= SVAL(data,0);
+           /* we currently have no way of doing this. Can any unix do it? */
+           if (i < place)      /* move down */;
+           else if (i > place )        /* move up */;
+         }
+#endif
+         desc.errcode=NERR_notsupported; /* not yet supported */
+         if (queue) free(queue);
+       }
+      }
+    else desc.errcode=NERR_JobNotFound;
+    break;
+  case 0xb:   /* change print job name, data gives the name */
+    /* jobid, snum should be zero */
+    if (isalpha(*s))
+      {
+       pstring name;
+       int l = 0;
+       while (l<64 && *s)
+         {
+           if (isalnum(*s) || strchr("-._",*s))
+             name[l++] = *s;
+           s++;
+         }      
+       name[l] = 0;
+       
+       DEBUG(3,("Setting print name to %s\n",name));
+       
+       for (i=0;i<MAX_OPEN_FILES;i++)
+         if (Files[i].open && Files[i].print_file)
+           {
+             pstring wd;
+             GetWd(wd);
+             unbecome_user();
+             
+             if (!become_user(Files[i].cnum,uid) || 
+                 !become_service(Files[i].cnum,True))
+               break;
+             
+             if (sys_rename(Files[i].name,name) == 0)
+               string_set(&Files[i].name,name);
+             break;
+           }
+      }
+    desc.errcode=NERR_Success;
+  
+    break;
+  default:                     /* not implemented */
+    return False;
+  }
+  SSVALS(*rparam,0,desc.errcode);
+  SSVAL(*rparam,2,0);          /* converter word */
+  
+  return(True);
+}
+
+
+/****************************************************************************
+  get info about the server
+  ****************************************************************************/
+static BOOL api_RNetServerGetInfo(int cnum,int uid, char *param,char *data,
+                                 int mdrcnt,int mprcnt,
+                                 char **rdata,char **rparam,
+                                 int *rdata_len,int *rparam_len)
+{
+  char *str1 = param+2;
+  char *str2 = skip_string(str1,1);
+  char *p = skip_string(str2,1);
+  int uLevel = SVAL(p,0);
+  char *p2;
+  int struct_len;
+
+  DEBUG(4,("NetServerGetInfo level %d\n",uLevel));
+
+  /* check it's a supported varient */
+  if (!prefix_ok(str1,"WrLh")) return False;
+  switch( uLevel ) {
+  case 0:
+    if (strcmp(str2,"B16") != 0) return False;
+    struct_len = 16;
+    break;
+  case 1:
+    if (strcmp(str2,"B16BBDz") != 0) return False;
+    struct_len = 26;
+    break;
+  case 2:
+    if (strcmp(str2,"B16BBDzDDDWWzWWWWWWWBB21zWWWWWWWWWWWWWWWWWWWWWWz")
+       != 0) return False;
+    struct_len = 134;
+    break;
+  case 3:
+    if (strcmp(str2,"B16BBDzDDDWWzWWWWWWWBB21zWWWWWWWWWWWWWWWWWWWWWWzDWz")
+       != 0) return False;
+    struct_len = 144;
+    break;
+  case 20:
+    if (strcmp(str2,"DN") != 0) return False;
+    struct_len = 6;
+    break;
+  case 50:
+    if (strcmp(str2,"B16BBDzWWzzz") != 0) return False;
+    struct_len = 42;
+    break;
+  default: return False;
+  }
+
+  *rdata_len = mdrcnt;
+  *rdata = REALLOC(*rdata,*rdata_len);
+
+  p = *rdata;
+  p2 = p + struct_len;
+  if (uLevel != 20) {
+    StrnCpy(p,local_machine,16);
+    strupper(p);
+  }
+  p += 16;
+  if (uLevel > 0)
+    {
+      struct srv_info_struct *servers=NULL;
+      int i,count;
+      pstring comment;
+      uint32 servertype=SV_TYPE_SERVER_UNIX|SV_TYPE_WORKSTATION|
+       SV_TYPE_SERVER|SV_TYPE_TIME_SOURCE;
+
+      strcpy(comment,lp_serverstring());
+
+      if ((count=get_server_info(SV_TYPE_ALL,&servers))>0) {
+       for (i=0;i<count;i++)
+         if (strequal(servers[i].name,local_machine)) {
+           servertype = servers[i].type;
+           strcpy(comment,servers[i].comment);     
+         }
+      }
+      if (servers) free(servers);
+
+      SCVAL(p,0,2);            /* version_major */
+      SCVAL(p,1,0);            /* version_minor */
+      SIVAL(p,2,servertype);
+      if (mdrcnt == struct_len) {
+       SIVAL(p,6,0);
+      } else {
+       SIVAL(p,6,PTR_DIFF(p2,*rdata));
+       standard_sub(cnum,comment);
+       StrnCpy(p2,comment,MAX(mdrcnt - struct_len,0));
+       p2 = skip_string(p2,1);
+      }
+    }
+  if (uLevel > 1)
+    {
+      return False;            /* not yet implemented */
+    }
+
+  *rdata_len = PTR_DIFF(p2,*rdata);
+
+  *rparam_len = 6;
+  *rparam = REALLOC(*rparam,*rparam_len);
+  SSVAL(*rparam,0,NERR_Success);
+  SSVAL(*rparam,2,0);          /* converter word */
+  SSVAL(*rparam,4,*rdata_len);
+
+  return(True);
+}
+
+
+/****************************************************************************
+  get info about the server
+  ****************************************************************************/
+static BOOL api_NetWkstaGetInfo(int cnum,int uid, char *param,char *data,
+                               int mdrcnt,int mprcnt,
+                               char **rdata,char **rparam,
+                               int *rdata_len,int *rparam_len)
+{
+  char *str1 = param+2;
+  char *str2 = skip_string(str1,1);
+  char *p = skip_string(str2,1);
+  char *p2;
+  extern pstring sesssetup_user;
+  int level = SVAL(p,0);
+
+  DEBUG(4,("NetWkstaGetInfo level %d\n",level));
+
+  *rparam_len = 6;
+  *rparam = REALLOC(*rparam,*rparam_len);
+
+  /* check it's a supported varient */
+  if (!(level==10 && strcsequal(str1,"WrLh") && strcsequal(str2,"zzzBBzz")))
+    return(False);
+
+  *rdata_len = mdrcnt + 1024;
+  *rdata = REALLOC(*rdata,*rdata_len);
+
+  SSVAL(*rparam,0,NERR_Success);
+  SSVAL(*rparam,2,0);          /* converter word */
+
+  p = *rdata;
+  p2 = p + 22;
+
+  SIVAL(p,0,PTR_DIFF(p2,*rdata));
+  strcpy(p2,local_machine);
+  p2 = skip_string(p2,1);
+  p += 4;
+
+  SIVAL(p,0,PTR_DIFF(p2,*rdata));
+  strcpy(p2,sesssetup_user);
+  p2 = skip_string(p2,1);
+  p += 4;
+
+  SIVAL(p,0,PTR_DIFF(p2,*rdata));
+  strcpy(p2,my_workgroup());
+  p2 = skip_string(p2,1);
+  p += 4;
+
+  SCVAL(p,0,2); /* major version?? */
+  SCVAL(p,1,1); /* minor version?? */
+  p += 2;
+
+  SIVAL(p,0,PTR_DIFF(p2,*rdata));
+  strcpy(p2,my_workgroup());   /* login domain?? */
+  p2 = skip_string(p2,1);
+  p += 4;
+
+  SIVAL(p,0,PTR_DIFF(p2,*rdata));
+  strcpy(p2,"");
+  p2 = skip_string(p2,1);
+  p += 4;
+
+  *rdata_len = PTR_DIFF(p2,*rdata);
+
+  SSVAL(*rparam,4,*rdata_len);
+
+  return(True);
+}
+
+
+/****************************************************************************
+  get info about a user
+  ****************************************************************************/
+
+#define USER_PRIV_GUEST 0
+#define USER_PRIV_USER 1
+#define USER_PRIV_ADMIN 2
+
+static BOOL api_RNetUserGetInfo(int cnum,int uid, char *param,char *data,
+                               int mdrcnt,int mprcnt,
+                               char **rdata,char **rparam,
+                               int *rdata_len,int *rparam_len)
+{
+  char *str1 = param+2;
+  char *str2 = skip_string(str1,1);
+  char *UserName = skip_string(str2,1);
+  char *p = skip_string(UserName,1);
+  int uLevel = SVAL(p,0);
+  char *p2;
+
+  *rparam_len = 6;
+  *rparam = REALLOC(*rparam,*rparam_len);
+
+  /* check it's a supported varient */
+  if (strcmp(str1,"zWrLh") != 0) return False;
+  switch( uLevel ) {
+  case 0: p2 = "B21"; break;
+  case 1: p2 = "B21BB16DWzzWz"; break;
+  case 2: p2 = "B21BB16DWzzWzDzzzzDDDDWb21WWzWW"; break;
+  case 10: p2 = "B21Bzzz"; break;
+  case 11: p2 = "B21BzzzWDDzzDDWWzWzDWb21W"; break;
+  default: return False;
+  }
+  if (strcmp(p2,str2) != 0) return False;
+
+  *rdata_len = mdrcnt + 1024;
+  *rdata = REALLOC(*rdata,*rdata_len);
+
+  SSVAL(*rparam,0,NERR_Success);
+  SSVAL(*rparam,2,0);          /* converter word */
+
+  p = *rdata;
+  p2 = p + 86;
+
+  memset(p,0,21);
+  strcpy(p,UserName);
+  if (uLevel > 0) {
+    SCVAL(p,21,0);
+    *p2 = 0;
+    if (uLevel >= 10) {
+      SIVAL(p,22,PTR_DIFF(p2,p)); /* comment */
+      strcpy(p2,"<Comment>");
+      p2 = skip_string(p2,1);
+      SIVAL(p,26,PTR_DIFF(p2,p)); /* user_comment */
+      strcpy(p2,"<UserComment>");
+      p2 = skip_string(p2,1);
+      SIVAL(p,30,PTR_DIFF(p2,p)); /* full name */
+      strcpy(p2,"<FullName>");
+      p2 = skip_string(p2,1);
+    }
+    if (uLevel == 11) {         /* modelled after NTAS 3.51 reply */
+      SSVAL(p,34,USER_PRIV_USER); /* user privilege */
+      SIVAL(p,36,0);           /* auth flags */
+      SIVALS(p,40,-1);         /* password age */
+      SIVAL(p,44,PTR_DIFF(p2,p)); /* home dir */
+      strcpy(p2,"\\\\%L\\HOMES");
+      standard_sub_basic(p2);
+      p2 = skip_string(p2,1);
+      SIVAL(p,48,PTR_DIFF(p2,p)); /* parms */
+      strcpy(p2,"");
+      p2 = skip_string(p2,1);
+      SIVAL(p,52,0);           /* last logon */
+      SIVAL(p,56,0);           /* last logoff */
+      SSVALS(p,60,-1);         /* bad pw counts */
+      SSVALS(p,62,-1);         /* num logons */
+      SIVAL(p,64,PTR_DIFF(p2,p)); /* logon server */
+      strcpy(p2,"\\\\*");
+      p2 = skip_string(p2,1);
+      SSVAL(p,68,0);           /* country code */
+
+      SIVAL(p,70,PTR_DIFF(p2,p)); /* workstations */
+      strcpy(p2,"");
+      p2 = skip_string(p2,1);
+
+      SIVALS(p,74,-1);         /* max storage */
+      SSVAL(p,78,168);         /* units per week */
+      SIVAL(p,80,PTR_DIFF(p2,p)); /* logon hours */
+      memset(p2,-1,21);
+      SCVAL(p2,21,0);           /* fix zero termination */
+      p2 = skip_string(p2,1);
+
+      SSVAL(p,84,0);           /* code page */
+    }
+    if (uLevel == 1 || uLevel == 2) {
+      memset(p+22,' ',16);     /* password */
+      SIVALS(p,38,-1);         /* password age */
+      SSVAL(p,42,USER_PRIV_ADMIN); /* user privilege */
+      SIVAL(p,44,PTR_DIFF(p2,*rdata)); /* home dir */
+      strcpy(p2,"\\\\%L\\HOMES");
+      standard_sub_basic(p2);
+      p2 = skip_string(p2,1);
+      SIVAL(p,48,PTR_DIFF(p2,*rdata)); /* comment */
+      *p2++ = 0;
+      SSVAL(p,52,0);           /* flags */
+      SIVAL(p,54,0);           /* script_path */
+      if (uLevel == 2) {
+       SIVAL(p,60,0);          /* auth_flags */
+       SIVAL(p,64,PTR_DIFF(p2,*rdata)); /* full_name */
+       strcpy(p2,"<Full Name>");
+       p2 = skip_string(p2,1);
+       SIVAL(p,68,0);          /* urs_comment */
+       SIVAL(p,72,PTR_DIFF(p2,*rdata)); /* parms */
+       strcpy(p2,"");
+       p2 = skip_string(p2,1);
+       SIVAL(p,76,0);          /* workstations */
+       SIVAL(p,80,0);          /* last_logon */
+       SIVAL(p,84,0);          /* last_logoff */
+       SIVALS(p,88,-1);                /* acct_expires */
+       SIVALS(p,92,-1);                /* max_storage */
+       SSVAL(p,96,168);        /* units_per_week */
+       SIVAL(p,98,PTR_DIFF(p2,*rdata)); /* logon_hours */
+       memset(p2,-1,21);
+       p2 += 21;
+       SSVALS(p,102,-1);       /* bad_pw_count */
+       SSVALS(p,104,-1);       /* num_logons */
+       SIVAL(p,106,PTR_DIFF(p2,*rdata)); /* logon_server */
+       strcpy(p2,"\\\\%L");
+       standard_sub_basic(p2);
+       p2 = skip_string(p2,1);
+       SSVAL(p,110,49);        /* country_code */
+       SSVAL(p,112,860);       /* code page */
+      }
+    }
+  }
+
+  *rdata_len = PTR_DIFF(p2,*rdata);
+
+  SSVAL(*rparam,4,*rdata_len); /* is this right?? */
+
+  return(True);
+}
+
+
+/*******************************************************************
+  get groups that a user is a member of
+  ******************************************************************/
+static BOOL api_NetUserGetGroups(int cnum,int uid, char *param,char *data,
+                                int mdrcnt,int mprcnt,
+                                char **rdata,char **rparam,
+                                int *rdata_len,int *rparam_len)
+{
+  char *str1 = param+2;
+  char *str2 = skip_string(str1,1);
+  char *UserName = skip_string(str2,1);
+  char *p = skip_string(UserName,1);
+  int uLevel = SVAL(p,0);
+  char *p2;
+  int count=0;
+
+  *rparam_len = 8;
+  *rparam = REALLOC(*rparam,*rparam_len);
+
+  /* check it's a supported varient */
+  if (strcmp(str1,"zWrLeh") != 0) return False;
+  switch( uLevel ) {
+  case 0: p2 = "B21"; break;
+  default: return False;
+  }
+  if (strcmp(p2,str2) != 0) return False;
+
+  *rdata_len = mdrcnt + 1024;
+  *rdata = REALLOC(*rdata,*rdata_len);
+
+  SSVAL(*rparam,0,NERR_Success);
+  SSVAL(*rparam,2,0);          /* converter word */
+
+  p = *rdata;
+
+  /* XXXX we need a real SAM database some day */
+  strcpy(p,"Users"); p += 21; count++;
+  strcpy(p,"Domain Users"); p += 21; count++;
+  strcpy(p,"Guests"); p += 21; count++;
+  strcpy(p,"Domain Guests"); p += 21; count++;
+
+  *rdata_len = PTR_DIFF(p,*rdata);
+
+  SSVAL(*rparam,4,count);      /* is this right?? */
+  SSVAL(*rparam,6,count);      /* is this right?? */
+
+  return(True);
+}
+
+
+static BOOL api_WWkstaUserLogon(int cnum,int uid, char *param,char *data,
+                               int mdrcnt,int mprcnt,
+                               char **rdata,char **rparam,
+                               int *rdata_len,int *rparam_len)
+{
+  char *str1 = param+2;
+  char *str2 = skip_string(str1,1);
+  char *p = skip_string(str2,1);
+  int uLevel;
+  struct pack_desc desc;
+  char* name;
+
+  uLevel = SVAL(p,0);
+  name = p + 2;
+
+  bzero(&desc,sizeof(desc));
+
+  DEBUG(3,("WWkstaUserLogon uLevel=%d name=%s\n",uLevel,name));
+
+  /* check it's a supported varient */
+  if (strcmp(str1,"OOWb54WrLh") != 0) return False;
+  if (uLevel != 1 || strcmp(str2,"WB21BWDWWDDDDDDDzzzD") != 0) return False;
+  if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+  desc.base = *rdata;
+  desc.buflen = mdrcnt;
+  desc.subformat = NULL;
+  desc.format = str2;
+  
+  
+
+  if (init_package(&desc,1,0)) {
+    PACKI(&desc,"W",0);                /* code */
+    PACKS(&desc,"B21",name);   /* eff. name */
+    PACKS(&desc,"B","");               /* pad */
+    PACKI(&desc,"W",
+         Connections[cnum].admin_user?USER_PRIV_ADMIN:USER_PRIV_USER);
+    PACKI(&desc,"D",0);                /* auth flags XXX */
+    PACKI(&desc,"W",0);                /* num logons */
+    PACKI(&desc,"W",0);                /* bad pw count */
+    PACKI(&desc,"D",-1);               /* last logon */
+    PACKI(&desc,"D",-1);               /* last logoff */
+    PACKI(&desc,"D",-1);               /* logoff time */
+    PACKI(&desc,"D",-1);               /* kickoff time */
+    PACKI(&desc,"D",0);                /* password age */
+    PACKI(&desc,"D",0);                /* password can change */
+    PACKI(&desc,"D",-1);               /* password must change */
+    {
+      fstring mypath;
+      strcpy(mypath,"\\\\");
+      strcat(mypath,local_machine);
+      strupper(mypath);
+      PACKS(&desc,"z",mypath); /* computer */
+    }
+    PACKS(&desc,"z",my_workgroup());/* domain */
+    PACKS(&desc,"z",lp_logon_script());                /* script path */
+    PACKI(&desc,"D",0);                /* reserved */
+  }
+
+  *rdata_len = desc.usedlen;
+  *rparam_len = 6;
+  *rparam = REALLOC(*rparam,*rparam_len);
+  SSVALS(*rparam,0,desc.errcode);
+  SSVAL(*rparam,2,0);
+  SSVAL(*rparam,4,desc.neededlen);
+
+  DEBUG(4,("WWkstaUserLogon: errorcode %d\n",desc.errcode));
+  return(True);
+}
+
+
+/****************************************************************************
+  api_WAccessGetUserPerms
+  ****************************************************************************/
+static BOOL api_WAccessGetUserPerms(int cnum,int uid, char *param,char *data,
+                                   int mdrcnt,int mprcnt,
+                                   char **rdata,char **rparam,
+                                   int *rdata_len,int *rparam_len)
+{
+  char *str1 = param+2;
+  char *str2 = skip_string(str1,1);
+  char *user = skip_string(str2,1);
+  char *resource = skip_string(user,1);
+
+  DEBUG(3,("WAccessGetUserPerms user=%s resource=%s\n",user,resource));
+
+  /* check it's a supported varient */
+  if (strcmp(str1,"zzh") != 0) return False;
+  if (strcmp(str2,"") != 0) return False;
+
+  *rparam_len = 6;
+  *rparam = REALLOC(*rparam,*rparam_len);
+  SSVALS(*rparam,0,0);         /* errorcode */
+  SSVAL(*rparam,2,0);          /* converter word */
+  SSVAL(*rparam,4,0x7f);       /* permission flags */
+
+  return(True);
+}
+
+/****************************************************************************
+  api_WPrintJobEnumerate
+  ****************************************************************************/
+static BOOL api_WPrintJobGetInfo(int cnum,int uid, char *param,char *data,
+                                int mdrcnt,int mprcnt,
+                                char **rdata,char **rparam,
+                                int *rdata_len,int *rparam_len)
+{
+  char *str1 = param+2;
+  char *str2 = skip_string(str1,1);
+  char *p = skip_string(str2,1);
+  int uJobId = SVAL(p,0);
+  int uLevel,cbBuf;
+  int count;
+  int i;
+  int snum;
+  int job;
+  struct pack_desc desc;
+  print_queue_struct *queue=NULL;
+  print_status_struct status;
+
+  uLevel = SVAL(p,2);
+  cbBuf = SVAL(p,4);
+
+  bzero(&desc,sizeof(desc));
+  bzero(&status,sizeof(status));
+
+  DEBUG(3,("WPrintJobGetInfo uLevel=%d uJobId=0x%X\n",uLevel,uJobId));
+
+  /* check it's a supported varient */
+  if (strcmp(str1,"WWrLh") != 0) return False;
+  if (!check_printjob_info(&desc,uLevel,str2)) return False;
+
+  snum = (unsigned int)uJobId >> 8; /*## valid serice number??*/
+  job = uJobId & 0xFF;
+
+  if (snum < 0 || !VALID_SNUM(snum)) return(False);
+
+  count = get_printqueue(snum,cnum,&queue,&status);
+  for (i = 0; i < count; i++) {
+    if ((queue[i].job % 0xFF) == job) break;
+  }
+  if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+  desc.base = *rdata;
+  desc.buflen = mdrcnt;
+
+  if (init_package(&desc,1,0)) {
+    if (i < count) {
+      fill_printjob_info(cnum,snum,uLevel,&desc,&queue[i],i);
+      *rdata_len = desc.usedlen;
+    }
+    else {
+      desc.errcode = NERR_JobNotFound;
+      *rdata_len = 0;
+    }
+  }
+
+  *rparam_len = 6;
+  *rparam = REALLOC(*rparam,*rparam_len);
+  SSVALS(*rparam,0,desc.errcode);
+  SSVAL(*rparam,2,0);
+  SSVAL(*rparam,4,desc.neededlen);
+
+  if (queue) free(queue);
+
+  DEBUG(4,("WPrintJobGetInfo: errorcode %d\n",desc.errcode));
+  return(True);
+}
+
+static BOOL api_WPrintJobEnumerate(int cnum,int uid, char *param,char *data,
+                                  int mdrcnt,int mprcnt,
+                                  char **rdata,char **rparam,
+                                  int *rdata_len,int *rparam_len)
+{
+  char *str1 = param+2;
+  char *str2 = skip_string(str1,1);
+  char *p = skip_string(str2,1);
+  char* name = p;
+  int uLevel,cbBuf;
+  int count;
+  int i, succnt=0;
+  int snum;
+  struct pack_desc desc;
+  print_queue_struct *queue=NULL;
+  print_status_struct status;
+
+  bzero(&desc,sizeof(desc));
+  bzero(&status,sizeof(status));
+
+  p = skip_string(p,1);
+  uLevel = SVAL(p,0);
+  cbBuf = SVAL(p,2);
+
+  DEBUG(3,("WPrintJobEnumerate uLevel=%d name=%s\n",uLevel,name));
+
+  /* check it's a supported varient */
+  if (strcmp(str1,"zWrLeh") != 0) return False;
+  if (uLevel > 2) return False;        /* defined only for uLevel 0,1,2 */
+  if (!check_printjob_info(&desc,uLevel,str2)) return False;
+
+  snum = lp_servicenumber(name);
+  if (snum < 0 && pcap_printername_ok(name,NULL)) {
+    int pnum = lp_servicenumber(PRINTERS_NAME);
+    if (pnum >= 0) {
+      lp_add_printer(name,pnum);
+      snum = lp_servicenumber(name);
+    }
+  }
+
+  if (snum < 0 || !VALID_SNUM(snum)) return(False);
+
+  count = get_printqueue(snum,cnum,&queue,&status);
+  if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+  desc.base = *rdata;
+  desc.buflen = mdrcnt;
+
+  if (init_package(&desc,count,0)) {
+    succnt = 0;
+    for (i = 0; i < count; i++) {
+      fill_printjob_info(cnum,snum,uLevel,&desc,&queue[i],i);
+      if (desc.errcode == NERR_Success) succnt = i+1;
+    }
+  }
+
+  *rdata_len = desc.usedlen;
+
+  *rparam_len = 8;
+  *rparam = REALLOC(*rparam,*rparam_len);
+  SSVALS(*rparam,0,desc.errcode);
+  SSVAL(*rparam,2,0);
+  SSVAL(*rparam,4,succnt);
+  SSVAL(*rparam,6,count);
+
+  if (queue) free(queue);
+
+  DEBUG(4,("WPrintJobEnumerate: errorcode %d\n",desc.errcode));
+  return(True);
+}
+
+static int check_printdest_info(struct pack_desc* desc,
+                               int uLevel, char* id)
+{
+  desc->subformat = NULL;
+  switch( uLevel ) {
+  case 0: desc->format = "B9"; break;
+  case 1: desc->format = "B9B21WWzW"; break;
+  case 2: desc->format = "z"; break;
+  case 3: desc->format = "zzzWWzzzWW"; break;
+  default: return False;
+  }
+  if (strcmp(desc->format,id) != 0) return False;
+  return True;
+}
+
+static void fill_printdest_info(int cnum, int snum, int uLevel,
+                               struct pack_desc* desc)
+{
+  char buf[100];
+  strcpy(buf,SERVICE(snum));
+  strupper(buf);
+  if (uLevel <= 1) {
+    PACKS(desc,"B9",buf);      /* szName */
+    if (uLevel == 1) {
+      PACKS(desc,"B21","");    /* szUserName */
+      PACKI(desc,"W",0);               /* uJobId */
+      PACKI(desc,"W",0);               /* fsStatus */
+      PACKS(desc,"z","");      /* pszStatus */
+      PACKI(desc,"W",0);               /* time */
+    }
+  }
+  if (uLevel == 2 || uLevel == 3) {
+    PACKS(desc,"z",buf);               /* pszPrinterName */
+    if (uLevel == 3) {
+      PACKS(desc,"z","");      /* pszUserName */
+      PACKS(desc,"z","");      /* pszLogAddr */
+      PACKI(desc,"W",0);               /* uJobId */
+      PACKI(desc,"W",0);               /* fsStatus */
+      PACKS(desc,"z","");      /* pszStatus */
+      PACKS(desc,"z","");      /* pszComment */
+      PACKS(desc,"z","NULL"); /* pszDrivers */
+      PACKI(desc,"W",0);               /* time */
+      PACKI(desc,"W",0);               /* pad1 */
+    }
+  }
+}
+
+static BOOL api_WPrintDestGetInfo(int cnum,int uid, char *param,char *data,
+                                 int mdrcnt,int mprcnt,
+                                 char **rdata,char **rparam,
+                                 int *rdata_len,int *rparam_len)
+{
+  char *str1 = param+2;
+  char *str2 = skip_string(str1,1);
+  char *p = skip_string(str2,1);
+  char* PrinterName = p;
+  int uLevel,cbBuf;
+  struct pack_desc desc;
+  int snum;
+
+  bzero(&desc,sizeof(desc));
+
+  p = skip_string(p,1);
+  uLevel = SVAL(p,0);
+  cbBuf = SVAL(p,2);
+
+  DEBUG(3,("WPrintDestGetInfo uLevel=%d PrinterName=%s\n",uLevel,PrinterName));
+
+  /* check it's a supported varient */
+  if (strcmp(str1,"zWrLh") != 0) return False;
+  if (!check_printdest_info(&desc,uLevel,str2)) return False;
+
+  snum = lp_servicenumber(PrinterName);
+  if (snum < 0 && pcap_printername_ok(PrinterName,NULL)) {
+    int pnum = lp_servicenumber(PRINTERS_NAME);
+    if (pnum >= 0) {
+      lp_add_printer(PrinterName,pnum);
+      snum = lp_servicenumber(PrinterName);
+    }
+  }
+
+  if (snum < 0) {
+    *rdata_len = 0;
+    desc.errcode = NERR_DestNotFound;
+    desc.neededlen = 0;
+  }
+  else {
+    if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+    desc.base = *rdata;
+    desc.buflen = mdrcnt;
+    if (init_package(&desc,1,0)) {
+      fill_printdest_info(cnum,snum,uLevel,&desc);
+    }
+    *rdata_len = desc.usedlen;
+  }
+
+  *rparam_len = 6;
+  *rparam = REALLOC(*rparam,*rparam_len);
+  SSVALS(*rparam,0,desc.errcode);
+  SSVAL(*rparam,2,0);
+  SSVAL(*rparam,4,desc.neededlen);
+
+  DEBUG(4,("WPrintDestGetInfo: errorcode %d\n",desc.errcode));
+  return(True);
+}
+
+static BOOL api_WPrintDestEnum(int cnum,int uid, char *param,char *data,
+                              int mdrcnt,int mprcnt,
+                              char **rdata,char **rparam,
+                              int *rdata_len,int *rparam_len)
+{
+  char *str1 = param+2;
+  char *str2 = skip_string(str1,1);
+  char *p = skip_string(str2,1);
+  int uLevel,cbBuf;
+  int queuecnt;
+  int i, n, succnt=0;
+  struct pack_desc desc;
+  int services = lp_numservices();
+
+  bzero(&desc,sizeof(desc));
+
+  uLevel = SVAL(p,0);
+  cbBuf = SVAL(p,2);
+
+  DEBUG(3,("WPrintDestEnum uLevel=%d\n",uLevel));
+
+  /* check it's a supported varient */
+  if (strcmp(str1,"WrLeh") != 0) return False;
+  if (!check_printdest_info(&desc,uLevel,str2)) return False;
+
+  queuecnt = 0;
+  for (i = 0; i < services; i++)
+    if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i))
+      queuecnt++;
+
+  if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+  desc.base = *rdata;
+  desc.buflen = mdrcnt;
+  if (init_package(&desc,queuecnt,0)) {    
+    succnt = 0;
+    n = 0;
+    for (i = 0; i < services; i++) {
+      if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i)) {
+       fill_printdest_info(cnum,i,uLevel,&desc);
+       n++;
+       if (desc.errcode == NERR_Success) succnt = n;
+      }
+    }
+  }
+
+  *rdata_len = desc.usedlen;
+
+  *rparam_len = 8;
+  *rparam = REALLOC(*rparam,*rparam_len);
+  SSVALS(*rparam,0,desc.errcode);
+  SSVAL(*rparam,2,0);
+  SSVAL(*rparam,4,succnt);
+  SSVAL(*rparam,6,queuecnt);
+
+  DEBUG(4,("WPrintDestEnumerate: errorcode %d\n",desc.errcode));
+  return(True);
+}
+
+static BOOL api_WPrintDriverEnum(int cnum,int uid, char *param,char *data,
+                                int mdrcnt,int mprcnt,
+                                char **rdata,char **rparam,
+                                int *rdata_len,int *rparam_len)
+{
+  char *str1 = param+2;
+  char *str2 = skip_string(str1,1);
+  char *p = skip_string(str2,1);
+  int uLevel,cbBuf;
+  int succnt;
+  struct pack_desc desc;
+
+  bzero(&desc,sizeof(desc));
+
+  uLevel = SVAL(p,0);
+  cbBuf = SVAL(p,2);
+
+  DEBUG(3,("WPrintDriverEnum uLevel=%d\n",uLevel));
+
+  /* check it's a supported varient */
+  if (strcmp(str1,"WrLeh") != 0) return False;
+  if (uLevel != 0 || strcmp(str2,"B41") != 0) return False;
+
+  if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+  desc.base = *rdata;
+  desc.buflen = mdrcnt;
+  if (init_package(&desc,1,0)) {
+    PACKS(&desc,"B41","NULL");
+  }
+
+  succnt = (desc.errcode == NERR_Success ? 1 : 0);
+
+  *rdata_len = desc.usedlen;
+
+  *rparam_len = 8;
+  *rparam = REALLOC(*rparam,*rparam_len);
+  SSVALS(*rparam,0,desc.errcode);
+  SSVAL(*rparam,2,0);
+  SSVAL(*rparam,4,succnt);
+  SSVAL(*rparam,6,1);
+
+  DEBUG(4,("WPrintDriverEnum: errorcode %d\n",desc.errcode));
+  return(True);
+}
+
+static BOOL api_WPrintQProcEnum(int cnum,int uid, char *param,char *data,
+                               int mdrcnt,int mprcnt,
+                               char **rdata,char **rparam,
+                               int *rdata_len,int *rparam_len)
+{
+  char *str1 = param+2;
+  char *str2 = skip_string(str1,1);
+  char *p = skip_string(str2,1);
+  int uLevel,cbBuf;
+  int succnt;
+  struct pack_desc desc;
+
+  bzero(&desc,sizeof(desc));
+
+  uLevel = SVAL(p,0);
+  cbBuf = SVAL(p,2);
+
+  DEBUG(3,("WPrintQProcEnum uLevel=%d\n",uLevel));
+
+  /* check it's a supported varient */
+  if (strcmp(str1,"WrLeh") != 0) return False;
+  if (uLevel != 0 || strcmp(str2,"B13") != 0) return False;
+
+  if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+  desc.base = *rdata;
+  desc.buflen = mdrcnt;
+  desc.format = str2;
+  if (init_package(&desc,1,0)) {
+    PACKS(&desc,"B13","lpd");
+  }
+
+  succnt = (desc.errcode == NERR_Success ? 1 : 0);
+
+  *rdata_len = desc.usedlen;
+
+  *rparam_len = 8;
+  *rparam = REALLOC(*rparam,*rparam_len);
+  SSVALS(*rparam,0,desc.errcode);
+  SSVAL(*rparam,2,0);
+  SSVAL(*rparam,4,succnt);
+  SSVAL(*rparam,6,1);
+
+  DEBUG(4,("WPrintQProcEnum: errorcode %d\n",desc.errcode));
+  return(True);
+}
+
+static BOOL api_WPrintPortEnum(int cnum,int uid, char *param,char *data,
+                              int mdrcnt,int mprcnt,
+                              char **rdata,char **rparam,
+                              int *rdata_len,int *rparam_len)
+{
+  char *str1 = param+2;
+  char *str2 = skip_string(str1,1);
+  char *p = skip_string(str2,1);
+  int uLevel,cbBuf;
+  int succnt;
+  struct pack_desc desc;
+
+  bzero(&desc,sizeof(desc));
+
+  uLevel = SVAL(p,0);
+  cbBuf = SVAL(p,2);
+
+  DEBUG(3,("WPrintPortEnum uLevel=%d\n",uLevel));
+
+  /* check it's a supported varient */
+  if (strcmp(str1,"WrLeh") != 0) return False;
+  if (uLevel != 0 || strcmp(str2,"B9") != 0) return False;
+
+  if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+  bzero(&desc,sizeof(desc));
+  desc.base = *rdata;
+  desc.buflen = mdrcnt;
+  desc.format = str2;
+  if (init_package(&desc,1,0)) {
+    PACKS(&desc,"B13","lp0");
+  }
+
+  succnt = (desc.errcode == NERR_Success ? 1 : 0);
+
+  *rdata_len = desc.usedlen;
+
+  *rparam_len = 8;
+  *rparam = REALLOC(*rparam,*rparam_len);
+  SSVALS(*rparam,0,desc.errcode);
+  SSVAL(*rparam,2,0);
+  SSVAL(*rparam,4,succnt);
+  SSVAL(*rparam,6,1);
+
+  DEBUG(4,("WPrintPortEnum: errorcode %d\n",desc.errcode));
+  return(True);
+}
+
+/****************************************************************************
+  the buffer was too small
+  ****************************************************************************/
+static BOOL api_TooSmall(int cnum,int uid, char *param,char *data,
+                        int mdrcnt,int mprcnt,
+                        char **rdata,char **rparam,
+                        int *rdata_len,int *rparam_len)
+{
+  *rparam_len = MIN(*rparam_len,mprcnt);
+  *rparam = REALLOC(*rparam,*rparam_len);
+
+  *rdata_len = 0;
+
+  SSVAL(*rparam,0,NERR_BufTooSmall);
+
+  DEBUG(3,("Supplied buffer too small in API command\n"));
+
+  return(True);
+}
+
+
+/****************************************************************************
+  the request is not supported
+  ****************************************************************************/
+static BOOL api_Unsupported(int cnum,int uid, char *param,char *data,
+                           int mdrcnt,int mprcnt,
+                           char **rdata,char **rparam,
+                           int *rdata_len,int *rparam_len)
+{
+  *rparam_len = 4;
+  *rparam = REALLOC(*rparam,*rparam_len);
+
+  *rdata_len = 0;
+
+  SSVAL(*rparam,0,NERR_notsupported);
+  SSVAL(*rparam,2,0);          /* converter word */
+
+  DEBUG(3,("Unsupported API command\n"));
+
+  return(True);
+}
+
+
+
+
+struct
+{
+  char *name;
+  int id;
+  BOOL (*fn)();
+  int flags;
+} api_commands[] = {
+  {"RNetShareEnum",    0,      api_RNetShareEnum,0},
+  {"RNetShareGetInfo", 1,      api_RNetShareGetInfo,0},
+  {"RNetServerGetInfo",        13,     api_RNetServerGetInfo,0},
+  {"RNetUserGetInfo",  56,     api_RNetUserGetInfo,0},
+  {"NetUserGetGroups", 59,     api_NetUserGetGroups,0},
+  {"NetWkstaGetInfo",  63,     api_NetWkstaGetInfo,0},
+  {"DosPrintQEnum",    69,     api_DosPrintQEnum,0},
+  {"DosPrintQGetInfo", 70,     api_DosPrintQGetInfo,0},
+  {"WPrintJobEnumerate",76,    api_WPrintJobEnumerate,0},
+  {"WPrintJobGetInfo", 77,     api_WPrintJobGetInfo,0},
+  {"RDosPrintJobDel",  81,     api_RDosPrintJobDel,0},
+  {"RDosPrintJobPause",        82,     api_RDosPrintJobDel,0},
+  {"RDosPrintJobResume",83,    api_RDosPrintJobDel,0},
+  {"WPrintDestEnum",   84,     api_WPrintDestEnum,0},
+  {"WPrintDestGetInfo",        85,     api_WPrintDestGetInfo,0},
+  {"NetRemoteTOD",     91,     api_NetRemoteTOD,0},
+  {"WPrintQueuePurge", 103,    api_WPrintQueuePurge,0},
+  {"NetServerEnum",    104,    api_RNetServerEnum,0},
+  {"WAccessGetUserPerms",105,  api_WAccessGetUserPerms,0},
+  {"SetUserPassword",  115,    api_SetUserPassword,0},
+  {"WWkstaUserLogon",  132,    api_WWkstaUserLogon,0},
+  {"PrintJobInfo",     147,    api_PrintJobInfo,0},
+  {"WPrintDriverEnum", 205,    api_WPrintDriverEnum,0},
+  {"WPrintQProcEnum",  206,    api_WPrintQProcEnum,0},
+  {"WPrintPortEnum",   207,    api_WPrintPortEnum,0},
+  {NULL,               -1,     api_Unsupported,0}};
+
+
+/****************************************************************************
+  handle remote api calls
+  ****************************************************************************/
+static int api_reply(int cnum,int uid,char *outbuf,char *data,char *params,
+                    int tdscnt,int tpscnt,int mdrcnt,int mprcnt)
+{
+  int api_command = SVAL(params,0);
+  char *rdata = NULL;
+  char *rparam = NULL;
+  int rdata_len = 0;
+  int rparam_len = 0;
+  BOOL reply=False;
+  int i;
+
+  DEBUG(3,("Got API command %d of form <%s> <%s> (tdscnt=%d,tpscnt=%d,mdrcnt=%d,mprcnt=%d)\n",
+          api_command,params+2,skip_string(params+2,1),
+          tdscnt,tpscnt,mdrcnt,mprcnt));
+
+  for (i=0;api_commands[i].name;i++)
+    if (api_commands[i].id == api_command && api_commands[i].fn)
+      {
+       DEBUG(3,("Doing %s\n",api_commands[i].name));
+       break;
+      }
+
+  rdata = (char *)malloc(1024); if (rdata) bzero(rdata,1024);
+  rparam = (char *)malloc(1024); if (rparam) bzero(rparam,1024);
+
+  reply = api_commands[i].fn(cnum,uid,params,data,mdrcnt,mprcnt,
+                            &rdata,&rparam,&rdata_len,&rparam_len);
+
+
+  if (rdata_len > mdrcnt ||
+      rparam_len > mprcnt)
+    {
+      reply = api_TooSmall(cnum,uid,params,data,mdrcnt,mprcnt,
+                          &rdata,&rparam,&rdata_len,&rparam_len);
+    }
+           
+
+  /* if we get False back then it's actually unsupported */
+  if (!reply)
+    api_Unsupported(cnum,uid,params,data,mdrcnt,mprcnt,
+                   &rdata,&rparam,&rdata_len,&rparam_len);
+
+      
+
+  /* now send the reply */
+  send_trans_reply(outbuf,rdata,rparam,NULL,rdata_len,rparam_len,0);
+
+  if (rdata)
+    free(rdata);
+  if (rparam)
+    free(rparam);
+  
+  return(-1);
+}
+
+/****************************************************************************
+  handle named pipe commands
+  ****************************************************************************/
+static int named_pipe(int cnum,int uid, char *outbuf,char *name,
+                     uint16 *setup,char *data,char *params,
+                     int suwcnt,int tdscnt,int tpscnt,
+                     int msrcnt,int mdrcnt,int mprcnt)
+{
+
+  if (strequal(name,"LANMAN"))
+    return(api_reply(cnum,uid,outbuf,data,params,tdscnt,tpscnt,mdrcnt,mprcnt));
+
+  DEBUG(3,("named pipe command on <%s> 0x%X setup1=%d\n",
+          name,(int)setup[0],(int)setup[1]));
+  
+  return(0);
+}
+
+
+/****************************************************************************
+  reply to a SMBtrans
+  ****************************************************************************/
+int reply_trans(char *inbuf,char *outbuf)
+{
+  fstring name;
+
+  char *data=NULL,*params=NULL;
+  uint16 *setup=NULL;
+
+  int outsize = 0;
+  int cnum = SVAL(inbuf,smb_tid);
+  int uid = SVAL(inbuf,smb_uid);
+
+  int tpscnt = SVAL(inbuf,smb_vwv0);
+  int tdscnt = SVAL(inbuf,smb_vwv1);
+  int mprcnt = SVAL(inbuf,smb_vwv2);
+  int mdrcnt = SVAL(inbuf,smb_vwv3);
+  int msrcnt = CVAL(inbuf,smb_vwv4);
+  BOOL close_on_completion = BITSETW(inbuf+smb_vwv5,0);
+  BOOL one_way = BITSETW(inbuf+smb_vwv5,1);
+  int pscnt = SVAL(inbuf,smb_vwv9);
+  int psoff = SVAL(inbuf,smb_vwv10);
+  int dscnt = SVAL(inbuf,smb_vwv11);
+  int dsoff = SVAL(inbuf,smb_vwv12);
+  int suwcnt = CVAL(inbuf,smb_vwv13);
+
+  StrnCpy(name,smb_buf(inbuf),sizeof(name)-1);
+  
+  if (tdscnt)
+    {
+      data = (char *)malloc(tdscnt);
+      memcpy(data,smb_base(inbuf)+dsoff,dscnt);
+    }
+  if (tpscnt)
+    {
+      params = (char *)malloc(tpscnt);
+      memcpy(params,smb_base(inbuf)+psoff,pscnt);
+    }
+
+  if (suwcnt)
+    {
+      int i;
+      setup = (uint16 *)malloc(suwcnt*sizeof(setup[0]));
+      for (i=0;i<suwcnt;i++)
+       setup[i] = SVAL(inbuf,smb_vwv14+i*SIZEOFWORD);
+    }
+
+
+  if (pscnt < tpscnt || dscnt < tdscnt)
+    {
+      /* We need to send an interim response then receive the rest
+        of the parameter/data bytes */
+      outsize = set_message(outbuf,0,0,True);
+      show_msg(outbuf);
+      send_smb(Client,outbuf);
+    }
+
+  /* receive the rest of the trans packet */
+  while (pscnt < tpscnt || dscnt < tdscnt)
+    {
+      int pcnt,poff,dcnt,doff,pdisp,ddisp;
+
+      receive_smb(Client,inbuf, 0);
+      show_msg(inbuf);
+         
+      /* Ensure this is still a trans packet (sanity check) */
+      if(CVAL(inbuf, smb_com) != SMBtrans)
+       {
+         DEBUG(2,("Invalid secondary trans2 packet\n"));
+         if (params) free(params);
+         if (data) free(data);
+         if (setup) free(setup);
+         return(ERROR(ERRSRV,ERRerror));
+       }
+      
+      tpscnt = SVAL(inbuf,smb_vwv0);
+      tdscnt = SVAL(inbuf,smb_vwv1);
+
+      pcnt = SVAL(inbuf,smb_vwv2);
+      poff = SVAL(inbuf,smb_vwv3);
+      pdisp = SVAL(inbuf,smb_vwv4);
+      
+      dcnt = SVAL(inbuf,smb_vwv5);
+      doff = SVAL(inbuf,smb_vwv6);
+      ddisp = SVAL(inbuf,smb_vwv7);
+      
+      pscnt += pcnt;
+      dscnt += dcnt;
+
+      if (pcnt)
+       memcpy(params+pdisp,smb_base(inbuf)+poff,pcnt);
+      if (dcnt)
+       memcpy(data+ddisp,smb_base(inbuf)+doff,dcnt);      
+    }
+
+
+  DEBUG(3,("trans <%s> data=%d params=%d setup=%d\n",name,tdscnt,tpscnt,suwcnt));
+  
+
+  if (strncmp(name,"\\PIPE\\",strlen("\\PIPE\\")) == 0)
+    outsize = named_pipe(cnum,uid,outbuf,name+strlen("\\PIPE\\"),setup,data,params,
+                        suwcnt,tdscnt,tpscnt,msrcnt,mdrcnt,mprcnt);
+
+
+  if (data) free(data);
+  if (params) free(params);
+  if (setup) free(setup);
+
+  if (close_on_completion)
+    close_cnum(cnum,uid);
+
+  if (one_way)
+    return(-1);
+  
+  if (outsize == 0)
+    return(ERROR(ERRSRV,ERRnosupport));
+
+  return(outsize);
+}
+
+
diff --git a/source/smbd/mangle.c b/source/smbd/mangle.c
new file mode 100644 (file)
index 0000000..8f1490c
--- /dev/null
@@ -0,0 +1,610 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   Name mangling
+   Copyright (C) Andrew Tridgell 1992-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"
+#include "loadparm.h"
+
+extern int DEBUGLEVEL;
+extern int case_default;
+extern BOOL case_mangle;
+
+/****************************************************************************
+provide a checksum on a string
+****************************************************************************/
+int str_checksum(char *s)
+{
+  int res = 0;
+  int c;
+  int i=0;
+  while (*s)
+    {
+      c = *s;
+      res ^= (c << (i % 15)) ^ (c >> (15-(i%15)));
+      s++; i++;
+    }
+  return(res);
+}
+
+/****************************************************************************
+return True if a name is a special msdos reserved name
+****************************************************************************/
+static BOOL is_reserved_msdos(char *fname)
+{
+  char upperFname[13];
+  char *p;
+
+  StrnCpy (upperFname, fname, 12);
+
+  /* lpt1.txt and con.txt etc are also illegal */
+  p=strchr(upperFname,'.');
+  if (p)
+   *p='\0';
+  strupper (upperFname);
+  if ((strcmp(upperFname,"CLOCK$") == 0) ||
+    (strcmp(upperFname,"CON") == 0) ||
+    (strcmp(upperFname,"AUX") == 0) ||
+    (strcmp(upperFname,"COM1") == 0) ||
+    (strcmp(upperFname,"COM2") == 0) ||
+    (strcmp(upperFname,"COM3") == 0) ||
+    (strcmp(upperFname,"COM4") == 0) ||
+    (strcmp(upperFname,"LPT1") == 0) ||
+    (strcmp(upperFname,"LPT2") == 0) ||
+    (strcmp(upperFname,"LPT3") == 0) ||
+    (strcmp(upperFname,"NUL") == 0) ||
+    (strcmp(upperFname,"PRN") == 0))
+      return (True) ;
+
+  return (False);
+}
+
+
+
+/****************************************************************************
+return True if a name is in 8.3 dos format
+****************************************************************************/
+BOOL is_8_3(char *fname)
+{
+  int len;
+  char *dot_pos;
+  char *slash_pos = strrchr(fname,'/');
+  int l;
+
+  if (slash_pos) fname = slash_pos+1;
+  len = strlen(fname);
+
+  dot_pos = strchr(fname,'.');
+
+  DEBUG(5,("checking %s for 8.3\n",fname));
+
+  if (case_mangle)
+    switch (case_default)
+      {
+      case CASE_LOWER:
+       if (strhasupper(fname)) return(False);
+       break;
+      case CASE_UPPER:
+       if (strhaslower(fname)) return(False);
+       break;
+      }
+
+  /* can't be longer than 12 chars */
+  if (len == 0 || len > 12)
+    return(False);
+
+  /* can't be an MS-DOS Special file such as lpt1 or even lpt1.txt */
+  if (is_reserved_msdos(fname))
+    return(False);
+
+  /* can't contain invalid dos chars */
+  /* Windows use the ANSI charset.
+     But filenames are translated in the PC charset.
+     This Translation may be more or less relaxed depending
+     the Windows application. */
+
+  /* %%% A nice improvment to name mangling would be to translate
+     filename to ANSI charset on the smb server host */
+
+  {
+    char *p = fname;
+#ifdef KANJI
+    dot_pos = 0;
+    while (*p)
+      {
+         if (is_shift_jis (*p)) {
+             p += 2;
+         } else if (is_kana (*p)) {
+             p ++;
+         } else {
+             if (*p == '.' && !dot_pos)
+                 dot_pos = (char *) p;
+             if (!isdoschar(*p))
+                 return(False);
+             p++;
+         }
+      }      
+#else
+    while (*p)
+      {
+       if (!isdoschar(*p))
+         return(False);
+       p++;
+      }      
+#endif /* KANJI */
+  }
+
+  /* no dot and less than 9 means OK */
+  if (!dot_pos)
+    return(len <= 8);
+       
+  l = PTR_DIFF(dot_pos,fname);
+
+  /* base must be at least 1 char except special cases . and .. */
+  if (l == 0)
+    return(strcmp(fname,".") == 0 || strcmp(fname,"..") == 0);
+
+  /* base can't be greater than 8 */
+  if (l > 8)
+    return(False);
+
+  if (lp_strip_dot() && 
+      len - l == 1 &&
+      !strchr(dot_pos+1,'.'))
+    {
+      *dot_pos = 0;
+      return(True);
+    }
+
+  /* extension must be between 1 and 3 */
+  if ( (len - l < 2 ) || (len - l > 4) )
+    return(False);
+
+  /* extension can't have a dot */
+  if (strchr(dot_pos+1,'.'))
+    return(False);
+
+  /* must be in 8.3 format */
+  return(True);
+}
+
+
+
+/*
+keep a stack of name mangling results - just
+so file moves and copies have a chance of working
+*/
+fstring *mangled_stack = NULL;
+int mangled_stack_size = 0;
+int mangled_stack_len = 0;
+
+/****************************************************************************
+create the mangled stack
+****************************************************************************/
+void create_mangled_stack(int size)
+{
+  if (mangled_stack)
+    {
+      free(mangled_stack);
+      mangled_stack_size = 0;
+      mangled_stack_len = 0;
+    }
+  if (size > 0)
+    mangled_stack = (fstring *)malloc(sizeof(fstring)*size);
+  if (mangled_stack) mangled_stack_size = size;
+}
+
+/****************************************************************************
+push a mangled name onto the stack
+****************************************************************************/
+static void push_mangled_name(char *s)
+{
+  int i;
+  char *p;
+
+  if (!mangled_stack)
+    return;
+
+  for (i=0;i<mangled_stack_len;i++)
+    if (strcmp(s,mangled_stack[i]) == 0)
+      {
+       array_promote(mangled_stack[0],sizeof(fstring),i);      
+       return;
+      }
+
+  memmove(mangled_stack[1],mangled_stack[0],
+         sizeof(fstring)*MIN(mangled_stack_len,mangled_stack_size-1));
+  strcpy(mangled_stack[0],s);
+  p = strrchr(mangled_stack[0],'.');
+  if (p && (!strhasupper(p+1)) && (strlen(p+1) < 4))
+    *p = 0;
+  mangled_stack_len = MIN(mangled_stack_size,mangled_stack_len+1);
+}
+
+/****************************************************************************
+check for a name on the mangled name stack
+****************************************************************************/
+BOOL check_mangled_stack(char *s)
+{
+  int i;
+  pstring tmpname;
+  char extension[5];
+  char *p = strrchr(s,'.');
+  BOOL check_extension = False;
+
+  extension[0] = 0;
+
+  if (!mangled_stack) return(False);
+
+  if (p)
+    {
+      check_extension = True;
+      StrnCpy(extension,p,4);
+      strlower(extension); /* XXXXXXX */
+    }
+
+  for (i=0;i<mangled_stack_len;i++)
+    {
+      strcpy(tmpname,mangled_stack[i]);
+      mangle_name_83(tmpname);
+      if (strequal(tmpname,s))
+       {
+         strcpy(s,mangled_stack[i]);
+         break;
+       }
+      if (check_extension && !strchr(mangled_stack[i],'.'))
+       {
+         strcpy(tmpname,mangled_stack[i]);
+         strcat(tmpname,extension);
+         mangle_name_83(tmpname);
+         if (strequal(tmpname,s))
+           {
+             strcpy(s,mangled_stack[i]);
+             strcat(s,extension);
+             break;
+           }     
+       }
+    }
+
+  if (i < mangled_stack_len)
+    {
+      DEBUG(3,("Found %s on mangled stack as %s\n",s,mangled_stack[i]));
+      array_promote(mangled_stack[0],sizeof(fstring),i);
+      return(True);      
+    }
+
+  return(False);
+}      
+
+static char *map_filename(char *s, /* This is null terminated */
+                         char *pattern, /* This isn't. */
+                         int len) /* This is the length of pattern. */
+{
+  static pstring matching_bit;  /* The bit of the string which matches */
+                                /* a * in pattern if indeed there is a * */
+  char *sp;                     /* Pointer into s. */
+  char *pp;                     /* Pointer into p. */
+  char *match_start;            /* Where the matching bit starts. */
+  pstring pat;
+
+  StrnCpy(pat, pattern, len);   /* Get pattern into a proper string! */
+  strcpy(matching_bit,"");      /* Match but no star gets this. */
+  pp = pat;                     /* Initialise the pointers. */
+  sp = s;
+  if ((len == 1) && (*pattern == '*')) {
+    return NULL;                /* Impossible, too ambiguous for */
+                                /* words! */
+  }
+
+  while ((*sp)                  /* Not the end of the string. */
+         && (*pp)               /* Not the end of the pattern. */
+         && (*sp == *pp)        /* The two match. */
+         && (*pp != '*')) {     /* No wildcard. */
+    sp++;                       /* Keep looking. */
+    pp++;
+  }
+  if (!*sp && !*pp)             /* End of pattern. */
+    return matching_bit;        /* Simple match.  Return empty string. */
+  if (*pp == '*') {
+    pp++;                       /* Always interrested in the chacter */
+                                /* after the '*' */
+    if (!*pp) {                 /* It is at the end of the pattern. */
+      StrnCpy(matching_bit, s, sp-s);
+      return matching_bit;
+    } else {
+      /* The next character in pattern must match a character further */
+      /* along s than sp so look for that character. */
+      match_start = sp;
+      while ((*sp)              /* Not the end of s. */
+             && (*sp != *pp))   /* Not the same  */
+        sp++;                   /* Keep looking. */
+      if (!*sp) {               /* Got to the end without a match. */
+        return NULL;
+      } else {                  /* Still hope for a match. */
+        /* Now sp should point to a matching character. */
+        StrnCpy(matching_bit, match_start, sp-match_start);
+        /* Back to needing a stright match again. */
+        while ((*sp)            /* Not the end of the string. */
+               && (*pp)         /* Not the end of the pattern. */
+               && (*sp == *pp)) { /* The two match. */
+          sp++;                 /* Keep looking. */
+          pp++;
+        }
+        if (!*sp && !*pp)       /* Both at end so it matched */
+          return matching_bit;
+        else
+          return NULL;
+      }
+    }
+  }
+  return NULL;                  /* No match. */
+}
+
+
+/* this is the magic char used for mangling */
+char magic_char = '~';
+
+
+/****************************************************************************
+determine whther is name could be a mangled name
+****************************************************************************/
+BOOL is_mangled(char *s)
+{
+  char *m = strchr(s,magic_char);
+  if (!m) return(False);
+
+  /* we use two base 36 chars efore the extension */
+  if (m[1] == '.' || m[1] == 0 ||
+      m[2] == '.' || m[2] == 0 ||
+      (m[3] != '.' && m[3] != 0))
+    return(is_mangled(m+1));
+
+  /* it could be */
+  return(True);
+}
+
+
+
+/****************************************************************************
+return a base 36 character. v must be from 0 to 35.
+****************************************************************************/
+static char base36(int v)
+{
+  v = v % 36;
+  if (v < 10)
+    return('0'+v);
+  else /* needed to work around a DEC C compiler bug */
+    return('A' + (v-10));
+}
+
+
+static void do_fwd_mangled_map(char *s, char *MangledMap)
+{
+  /* MangledMap is a series of name pairs in () separated by spaces.
+   * If s matches the first of the pair then the name given is the
+   * second of the pair.  A * means any number of any character and if
+   * present in the second of the pair as well as the first the
+   * matching part of the first string takes the place of the * in the
+   * second.
+   *
+   * I wanted this so that we could have RCS files which can be used
+   * by UNIX and DOS programs.  My mapping string is (RCS rcs) which
+   * converts the UNIX RCS file subdirectory to lowercase thus
+   * preventing mangling.
+   */
+  char *start=MangledMap;       /* Use this to search for mappings. */
+  char *end;                    /* Used to find the end of strings. */
+  char *match_string;
+  pstring new_string;           /* Make up the result here. */
+  char *np;                     /* Points into new_string. */
+
+  DEBUG(5,("Mangled Mapping '%s' map '%s'\n", s, MangledMap));
+  while (*start) {
+    while ((*start) && (*start != '('))
+      start++;
+    start++;                    /* Skip the ( */
+    if (!*start)
+      continue;                 /* Always check for the end. */
+    end = start;                /* Search for the ' ' or a ')' */
+    DEBUG(5,("Start of first in pair '%s'\n", start));
+    while ((*end) && !((*end == ' ') || (*end == ')')))
+      end++;
+    if (!*end) {
+      start = end;
+      continue;                 /* Always check for the end. */
+    }
+    DEBUG(5,("End of first in pair '%s'\n", end));
+    if ((match_string = map_filename(s, start, end-start))) {
+      DEBUG(5,("Found a match\n"));
+      /* Found a match. */
+      start = end+1;            /* Point to start of what it is to become. */
+      DEBUG(5,("Start of second in pair '%s'\n", start));
+      end = start;
+      np = new_string;
+      while ((*end)             /* Not the end of string. */
+             && (*end != ')')   /* Not the end of the pattern. */
+             && (*end != '*'))  /* Not a wildcard. */
+        *np++ = *end++;
+      if (!*end) {
+        start = end;
+        continue;               /* Always check for the end. */
+      }
+      if (*end == '*') {
+        strcpy(np, match_string);
+        np += strlen(match_string);
+        end++;                  /* Skip the '*' */
+        while ((*end)             /* Not the end of string. */
+               && (*end != ')')   /* Not the end of the pattern. */
+               && (*end != '*'))  /* Not a wildcard. */
+          *np++ = *end++;
+      }
+      if (!*end) {
+        start = end;
+        continue;               /* Always check for the end. */
+      }
+      *np++ = '\0';             /* NULL terminate it. */
+      DEBUG(5,("End of second in pair '%s'\n", end));
+      strcpy(s, new_string);    /* Substitute with the new name. */
+      DEBUG(5,("s is now '%s'\n", s));
+    }
+    start = end;              /* Skip a bit which cannot be wanted */
+    /* anymore. */
+    start++;
+  }
+}
+
+/****************************************************************************
+do the actual mangling to 8.3 format
+****************************************************************************/
+void mangle_name_83(char *s)
+{
+  int csum = str_checksum(s);
+  char *p;
+  char extension[4];
+  char base[9];
+  int baselen = 0;
+  int extlen = 0;
+
+  extension[0]=0;
+  base[0]=0;
+
+  p = strrchr(s,'.');  
+  if (p && (strlen(p+1)<4) )
+    {
+      BOOL all_normal = (strisnormal(p+1)); /* XXXXXXXXX */
+      if (all_normal && p[1] != 0)
+       {
+         *p = 0;
+         csum = str_checksum(s);
+         *p = '.';
+       }
+    }
+      
+
+  strupper(s);
+
+  DEBUG(5,("Mangling name %s to ",s));
+
+  if (p)
+    {
+      if (p == s)
+       strcpy(extension,"___");
+      else
+       {
+         *p++ = 0;
+         while (*p && extlen < 3)
+           {
+             if (isdoschar(*p) && *p != '.')
+               extension[extlen++] = *p;
+             p++;
+           }
+         extension[extlen] = 0;
+       }
+    }
+
+  p = s;
+
+  while (*p && baselen < 5)
+    {
+      if (isdoschar(*p) && *p != '.')
+       base[baselen++] = *p;
+      p++;
+    }
+  base[baselen] = 0;
+
+  csum = csum % (36*36);
+
+    sprintf(s,"%s%c%c%c",base,magic_char,base36(csum/36),base36(csum%36));
+
+  if (*extension)
+    {
+      strcat(s,".");
+      strcat(s,extension);
+    }
+  DEBUG(5,("%s\n",s));
+}
+
+
+
+/*******************************************************************
+  work out if a name is illegal, even for long names
+  ******************************************************************/
+static BOOL illegal_name(char *name)
+{
+  static unsigned char illegal[256];
+  static BOOL initialised=False;
+  unsigned char *s;
+
+  if (!initialised) {
+    char *ill = "*\\/?<>|\":{}";
+    initialised = True;
+  
+    bzero((char *)illegal,256);
+    for (s = (unsigned char *)ill; *s; s++)
+      illegal[*s] = True;
+  }
+
+#ifdef KANJI
+  for (s = (unsigned char *)name; *s;) {
+    if (is_shift_jis (*s)) {
+      s += 2;
+    } else if (illegal[*s]) {
+      return(True);
+    } else {
+      s++;
+    }
+  }
+#else
+  for (s = (unsigned char *)name;*s;s++)
+    if (illegal[*s]) return(True);
+#endif
+
+
+  return(False);
+}
+
+
+/****************************************************************************
+convert a filename to DOS format. return True if successful.
+****************************************************************************/
+BOOL name_map_mangle(char *OutName,BOOL need83,int snum)
+{
+#ifdef MANGLE_LONG_FILENAMES
+  if (!need83 && illegal_name(OutName)) need83 = True;
+#endif  
+
+  /* apply any name mappings */
+  {
+    char *map = lp_mangled_map(snum);
+    if (map && *map)
+      do_fwd_mangled_map(OutName,map);
+  }
+
+  /* check if it's already in 8.3 format */
+  if (need83 && !is_8_3(OutName)) {
+    if (!lp_manglednames(snum)) return(False);
+
+    /* mangle it into 8.3 */
+    push_mangled_name(OutName);  
+    mangle_name_83(OutName);
+  }
+  
+  return(True);
+}
+
diff --git a/source/smbd/message.c b/source/smbd/message.c
new file mode 100644 (file)
index 0000000..6a96b4c
--- /dev/null
@@ -0,0 +1,204 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   SMB messaging
+   Copyright (C) Andrew Tridgell 1992-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.
+*/
+/*
+   This file handles the messaging system calls for winpopup style
+   messages
+*/
+
+
+#include "includes.h"
+#include "loadparm.h"
+
+/* look in server.c for some explanation of these variables */
+extern int DEBUGLEVEL;
+
+
+static char msgbuf[1600];
+static int msgpos=0;
+static fstring msgfrom="";
+static fstring msgto="";
+
+/****************************************************************************
+deliver the message
+****************************************************************************/
+static void msg_deliver(void)
+{
+  pstring s;
+  fstring name;
+  FILE *f;
+  int i;
+
+  if (! (*lp_msg_command()))
+    {
+      DEBUG(1,("no messaging command specified\n"));
+      msgpos = 0;
+      return;
+    }
+
+  /* put it in a temporary file */
+  sprintf(s,"/tmp/msg.XXXXXX");
+  strcpy(name,(char *)mktemp(s));
+
+  f = fopen(name,"w");
+  if (!f)
+    {
+      DEBUG(1,("can't open message file %s\n",name));
+      return;
+    }
+
+  for (i=0;i<msgpos;)
+    {
+      if (msgbuf[i]=='\r' && i<(msgpos-1) && msgbuf[i+1]=='\n')
+       i++;
+      fputc(msgbuf[i++],f);
+    }
+
+  fclose(f);
+
+
+  /* run the command */
+  if (*lp_msg_command())
+    {
+      strcpy(s,lp_msg_command());
+      string_sub(s,"%s",name);
+      string_sub(s,"%f",msgfrom);
+      string_sub(s,"%t",msgto);
+      standard_sub(-1,s);
+      smbrun(s,NULL);
+    }
+
+  msgpos = 0;
+}
+
+
+
+/****************************************************************************
+  reply to a sends
+****************************************************************************/
+int reply_sends(char *inbuf,char *outbuf)
+{
+  int len;
+  char *orig,*dest,*msg;
+  int outsize = 0;
+
+  msgpos = 0;
+
+
+  if (! (*lp_msg_command()))
+    return(ERROR(ERRSRV,ERRmsgoff));
+
+  outsize = set_message(outbuf,0,0,True);
+
+  orig = smb_buf(inbuf)+1;
+  dest = skip_string(orig,1)+1;
+  msg = skip_string(dest,1)+1;
+
+  strcpy(msgfrom,orig);
+  strcpy(msgto,dest);
+
+  len = SVAL(msg,0);
+  len = MIN(len,1600-msgpos);
+
+  memcpy(&msgbuf[msgpos],msg+2,len);
+  msgpos += len;
+
+  DEBUG(3,("%s SMBsends (from %s to %s)\n",timestring(),orig,dest));
+
+  msg_deliver();
+
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a sendstrt
+****************************************************************************/
+int reply_sendstrt(char *inbuf,char *outbuf)
+{
+  char *orig,*dest;
+  int outsize = 0;
+
+  if (! (*lp_msg_command()))
+    return(ERROR(ERRSRV,ERRmsgoff));
+
+  outsize = set_message(outbuf,1,0,True);
+
+  msgpos = 0;
+
+  orig = smb_buf(inbuf)+1;
+  dest = skip_string(orig,1)+1;
+
+  strcpy(msgfrom,orig);
+  strcpy(msgto,dest);
+
+  DEBUG(3,("%s SMBsendstrt (from %s to %s)\n",timestring(),orig,dest));
+
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a sendtxt
+****************************************************************************/
+int reply_sendtxt(char *inbuf,char *outbuf)
+{
+  int len;
+  int outsize = 0;
+  char *msg;
+
+  if (! (*lp_msg_command()))
+    return(ERROR(ERRSRV,ERRmsgoff));
+
+  outsize = set_message(outbuf,0,0,True);
+
+  msg = smb_buf(inbuf) + 1;
+
+  len = SVAL(msg,0);
+  len = MIN(len,1600-msgpos);
+
+  memcpy(&msgbuf[msgpos],msg+2,len);
+  msgpos += len;
+
+  DEBUG(3,("%s SMBsendtxt\n",timestring()));
+
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a sendend
+****************************************************************************/
+int reply_sendend(char *inbuf,char *outbuf)
+{
+  int outsize = 0;
+
+  if (! (*lp_msg_command()))
+    return(ERROR(ERRSRV,ERRmsgoff));
+
+  outsize = set_message(outbuf,0,0,True);
+
+  DEBUG(3,("%s SMBsendend\n",timestring()));
+
+  msg_deliver();
+
+  return(outsize);
+}
+
diff --git a/source/smbd/password.c b/source/smbd/password.c
new file mode 100644 (file)
index 0000000..87c1fef
--- /dev/null
@@ -0,0 +1,1416 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   Password and authentication handling
+   Copyright (C) Andrew Tridgell 1992-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"
+#include "loadparm.h"
+
+extern int DEBUGLEVEL;
+extern int Protocol;
+
+/* users from session setup */
+static pstring session_users="";
+
+/* these are kept here to keep the string_combinations function simple */
+static char this_user[100]="";
+static char this_salt[100]="";
+static char this_crypted[100]="";
+
+#ifdef SMB_PASSWD
+/* Data to do lanman1/2 password challenge. */
+static unsigned char saved_challenge[8];
+static BOOL challenge_sent=False;
+
+/*******************************************************************
+Get the next challenge value - no repeats.
+********************************************************************/
+void generate_next_challenge(char *challenge)
+{
+  extern void E1(char *,char *,char *);
+  static int counter = 0;
+  struct timeval tval;
+  int v1,v2;
+  GetTimeOfDay(&tval);
+  v1 = (counter++) + getpid() + tval.tv_sec;
+  v2 = (counter++) * getpid() + tval.tv_usec;
+  SIVAL(challenge,0,v1);
+  SIVAL(challenge,4,v2);
+  E1(challenge,"SAMBA",saved_challenge);
+  memcpy(challenge,saved_challenge,8);
+  challenge_sent = True;
+}
+
+/*******************************************************************
+set the last challenge sent, usually from a password server
+********************************************************************/
+BOOL set_challenge(char *challenge)
+{
+  memcpy(saved_challenge,challenge,8);
+  challenge_sent = True;
+  return(True);
+}
+
+/*******************************************************************
+get the last challenge sent
+********************************************************************/
+BOOL last_challenge(char *challenge)
+{
+  if (!challenge_sent) return(False);
+  memcpy(challenge,saved_challenge,8);
+  return(True);
+}
+#endif
+
+/* this holds info on user ids that are already validated for this VC */
+static user_struct *validated_users = NULL;
+static int num_validated_users = 0;
+
+/****************************************************************************
+check if a uid has been validated, and return an index if it has. -1 if not
+****************************************************************************/
+int valid_uid(int uid)
+{
+  int i;
+  if (uid == -1) return(-1);
+
+  for (i=0;i<num_validated_users;i++)
+    if (validated_users[i].uid == uid)
+      {
+       DEBUG(3,("valid uid %d mapped to vuid %d (user=%s)\n",
+                uid,i,validated_users[i].name));
+       return(i);
+      }
+  return(-1);
+}
+
+/****************************************************************************
+check if a uid has been validated, and return an pointer to the user_struct
+if it has. NULL if not
+****************************************************************************/
+user_struct *get_valid_user_struct(int uid)
+{
+  int vuid = valid_uid(uid);
+  if(vuid == -1 || validated_users[vuid].guest)
+    return NULL;
+  return &validated_users[vuid];
+}
+
+/****************************************************************************
+invalidate a uid
+****************************************************************************/
+void invalidate_uid(int uid)
+{
+  int i;
+  for (i=0;i<num_validated_users;i++)
+    if (validated_users[i].uid == uid)
+      {
+       user_struct *vuser = &validated_users[i];
+       vuser->uid = -1;
+       vuser->gid = -1;
+       vuser->user_ngroups = 0;
+       if(vuser->user_groups && 
+          (vuser->user_groups != (gid_t *)vuser->user_igroups))
+         free(vuser->user_groups);
+       vuser->user_groups = NULL;
+       if(vuser->user_igroups)
+         free(vuser->user_igroups);
+       vuser->user_igroups = NULL;
+      }
+}
+
+
+/****************************************************************************
+return a validated username
+****************************************************************************/
+char *validated_username(int vuid)
+{
+  return(validated_users[vuid].name);
+}
+
+/****************************************************************************
+register a uid/name pair as being valid and that a valid password
+has been given.
+****************************************************************************/
+void register_uid(int uid,int gid, char *name,BOOL guest)
+{
+  user_struct *vuser;
+
+  if (valid_uid(uid) >= 0)
+    return;
+  validated_users = (user_struct *)Realloc(validated_users,
+                                          sizeof(user_struct)*
+                                          (num_validated_users+1));
+
+  if (!validated_users)
+    {
+      DEBUG(0,("Failed to realloc users struct!\n"));
+      return;
+    }
+
+  vuser = &validated_users[num_validated_users];
+  vuser->uid = uid;
+  vuser->gid = gid;
+  vuser->guest = guest;
+  strcpy(vuser->name,name);
+
+  vuser->user_ngroups = 0;
+  vuser->user_groups = NULL;
+  vuser->user_igroups = NULL;
+
+  /* Find all the groups this uid is in and store them. 
+     Used by become_user() */
+  setup_groups(name,uid,gid,
+              &vuser->user_ngroups,
+              &vuser->user_igroups,
+              &vuser->user_groups);
+
+  DEBUG(3,("uid %d registered to name %s\n",uid,name));
+  
+  num_validated_users++;
+}
+
+
+/****************************************************************************
+add a name to the session users list
+****************************************************************************/
+void add_session_user(char *user)
+{
+  fstring suser;
+  StrnCpy(suser,user,sizeof(suser)-1);
+
+  if (!Get_Pwnam(suser,True)) return;
+
+  if (suser && *suser && !in_list(suser,session_users,False))
+    {
+      if (strlen(suser) + strlen(session_users) + 2 >= sizeof(pstring))
+       DEBUG(1,("Too many session users??\n"));
+      else
+       {
+         strcat(session_users," ");
+         strcat(session_users,suser);
+       }
+    }
+}
+
+
+#ifdef NO_GETSPNAM
+/* a fake shadow password routine which just fills a fake spwd struct
+ * with the sp_pwdp field. (sreiz@aie.nl)
+ */
+static struct spwd *getspnam(char *username) /* fake shadow password routine */
+{
+       FILE *f;
+       char line[1024];
+       static char pw[20];
+       static struct spwd static_spwd;
+
+       static_spwd.sp_pwdp=0;
+       if (!(f=fopen("/etc/master.passwd", "r")))
+               return 0;
+       while (fgets(line, 1024, f)) {
+               if (!strncmp(line, username, strlen(username)) &&
+                line[strlen(username)]==':') { /* found entry */
+                       char *p, *q;
+
+                       p=line+strlen(username)+1;
+                       if ((q=strchr(p, ':'))) {
+                               *q=0;
+                               if (q-p+1>20)
+                                       break;
+                               strcpy(pw, p);
+                               static_spwd.sp_pwdp=pw;
+                       }
+                       break;
+               }
+       }
+       fclose(f);
+       if (static_spwd.sp_pwdp)
+               return &static_spwd;
+       return 0;
+}
+#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);
+
+  for (i=0; i<parts;i++)
+    {
+      p1 = crypt(p2,salt);
+      strcat(result,p1+2);
+      StrnCpy(salt,&result[2+i*AUTH_CIPHERTEXT_SEG_CHARS],2);
+      p2 += AUTH_CLEARTEXT_SEG_CHARS;
+    }
+
+  return(result);
+}
+#endif
+
+
+/****************************************************************************
+update the enhanced security database. Only relevant for OSF1 at the moment.
+****************************************************************************/
+static void update_protected_database( char *user, BOOL result)
+{
+#ifdef OSF1_ENH_SEC
+  struct pr_passwd *mypasswd;
+  time_t starttime;
+  long tz;
+
+  mypasswd = getprpwnam (user);
+  starttime = time (NULL);
+  tz = mktime ( localtime ( &starttime ) );
+
+  if (result)
+    {
+      mypasswd->ufld.fd_slogin = tz;
+      mypasswd->ufld.fd_nlogins = 0;
+      
+      putprpwnam(user,mypasswd);
+      
+      DEBUG(3,("Update protected database for Account %s after succesful connection\n",user));
+    }
+  else
+    {
+      mypasswd->ufld.fd_ulogin = tz;
+      mypasswd->ufld.fd_nlogins = mypasswd->ufld.fd_nlogins + 1;
+      if ( mypasswd->ufld.fd_max_tries != 0 && mypasswd->ufld.fd_nlogins > mypasswd->ufld.fd_max_tries )
+       {
+         mypasswd->uflg.fg_lock = 0;
+         DEBUG(3,("Account is disabled -- see Account Administrator.\n"));
+       }
+      putprpwnam ( user , mypasswd );
+      DEBUG(3,("Update protected database for Account %s after refusing connection\n",user));
+    }
+#else
+  DEBUG(6,("Updated database with %s %s\n",user,BOOLSTR(result)));
+#endif
+}
+
+
+#ifdef AFS_AUTH
+/*******************************************************************
+check on AFS authentication
+********************************************************************/
+static BOOL afs_auth(char *this_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,
+                                this_user,
+                                (char *) 0, /* instance */
+                                (char *) 0, /* cell */
+                                password,
+                                0,          /* lifetime, default */
+                                &password_expires, /*days 'til it expires */
+                                0,          /* spare 2 */
+                                &reason) == 0)
+    return(True);
+  return(False);
+}
+#endif
+
+
+#ifdef DFS_AUTH
+
+sec_login_handle_t my_dce_sec_context;
+int dcelogin_atmost_once = 0;
+
+/*******************************************************************
+check on a DCE/DFS authentication
+********************************************************************/
+static BOOL dfs_auth(char *this_user,char *password)
+{
+  error_status_t err;
+  int err2;
+  int prterr;
+  boolean32 password_reset;
+  sec_passwd_rec_t my_dce_password;
+  sec_login_auth_src_t auth_src = sec_login_auth_src_network;
+  unsigned char dce_errstr[dce_c_error_string_len];
+
+  /*
+   * 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) ||
+      dcelogin_atmost_once)
+    return(False);
+
+  if (sec_login_setup_identity(
+                              (unsigned char *)this_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",
+              this_user,dce_errstr));
+      return(False);
+    }
+
+  my_dce_password.version_number = sec_passwd_c_version_none;
+  my_dce_password.pepper = NULL; 
+  my_dce_password.key.key_type = sec_passwd_plain;
+  my_dce_password.key.tagged_union.plain  = (idl_char *)password;
+  
+  if (sec_login_valid_and_cert_ident(my_dce_sec_context,
+                                    &my_dce_password,
+                                    &password_reset,
+                                    &auth_src,
+                                    &err) == 0 )
+    { 
+      dce_error_inq_text(err, dce_errstr, &err2);
+      DEBUG(0,("DCE Identity Validation failed for principal %s: %s\n",
+              this_user,dce_errstr));
+         
+      return(False);
+    }
+
+  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",
+              this_user,dce_errstr));
+      sec_login_purge_context(my_dce_sec_context, &err);
+      return(False);
+    }
+  else
+    {
+      DEBUG(0,("DCE login succeeded for principal %s on pid %d\n",
+              this_user, getpid()));
+    }
+
+  dcelogin_atmost_once = 1;
+  return (True);
+}
+
+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",
+              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
+
+
+/****************************************************************************
+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 BOOL string_combinations2(char *s,int offset,BOOL (*fn)(),int N)
+{
+  int len = strlen(s);
+  int i;
+
+#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 (string_combinations2(s,i+1,fn,N-1))
+       return(True);
+      s[i] = c;
+    }
+  return(False);
+}
+
+/****************************************************************************
+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 BOOL string_combinations(char *s,BOOL (*fn)(),int N)
+{
+  int n;
+  for (n=1;n<=N;n++)
+    if (string_combinations2(s,0,fn,n)) return(True);
+  return(False);
+}
+
+
+
+/****************************************************************************
+core of password checking routine
+****************************************************************************/
+BOOL password_check(char *password)
+{
+#ifdef AFS_AUTH
+  if (afs_auth(this_user,password)) return(True);
+#endif
+
+#ifdef DFS_AUTH
+  if (dfs_auth(this_user,password)) return(True);
+#endif 
+
+#ifdef PWDAUTH
+  if (pwdauth(this_user,password) == 0)
+    return(True);
+#endif
+
+#ifdef OSF1_ENH_SEC
+  return(strcmp(osf1_bigcrypt(password,this_salt),this_crypted) == 0);
+#endif
+
+#ifdef ULTRIX_AUTH
+  return (strcmp((char *)crypt16(password, this_salt ),this_crypted) == 0);
+#endif
+
+#ifdef LINUX_BIGCRYPT
+  return(linux_bigcrypt(password,this_salt,this_crypted));
+#endif
+
+#ifdef NO_CRYPT
+  DEBUG(1,("Warning - no crypt available\n"));
+  return(False);
+#else
+  return(strcmp((char *)crypt(password,this_salt),this_crypted) == 0);
+#endif
+}
+
+#ifdef SMB_PASSWD
+/****************************************************************************
+core of smb password checking routine.
+****************************************************************************/
+BOOL smb_password_check(char *password, unsigned char *part_passwd, unsigned char *c8)
+{
+  /* Finish the encryption of part_passwd. */
+  unsigned char p21[21];
+  unsigned char p24[24];
+
+  if(part_passwd == NULL)
+    DEBUG(10,("No password set - allowing access\n"));
+  /* No password set - always true ! */
+  if(part_passwd == NULL)
+    return 1;
+
+  memset(p21,'\0',21);
+  memcpy(p21,part_passwd,16);
+  E_P24(p21, c8, p24);
+#if DEBUG_PASSWORD
+  {
+    int i;
+    DEBUG(100,("Part password (P16) was |"));
+    for(i = 0; i < 16; i++)
+      DEBUG(100,("%X ", (unsigned char)part_passwd[i]));
+    DEBUG(100,("|\n"));
+    DEBUG(100,("Password from client was |"));
+    for(i = 0; i < 24; i++)
+      DEBUG(100,("%X ", (unsigned char)password[i]));
+    DEBUG(100,("|\n"));
+    DEBUG(100,("Given challenge was |"));
+    for(i = 0; i < 8; i++)
+      DEBUG(100,("%X ", (unsigned char)c8[i]));
+    DEBUG(100,("|\n"));
+    DEBUG(100,("Value from encryption was |"));
+    for(i = 0; i < 24; i++)
+      DEBUG(100,("%X ", (unsigned char)p24[i]));
+    DEBUG(100,("|\n"));
+  }
+#endif
+  return (memcmp(p24, password, 24) == 0);
+}
+#endif
+
+/****************************************************************************
+check if a username/password is OK
+****************************************************************************/
+BOOL password_ok(char *user,char *password, int pwlen, struct passwd *pwd, BOOL is_nt_password)
+{
+  pstring pass2;
+  int level = lp_passwordlevel();
+  struct passwd *pass;
+#ifdef SMB_PASSWD
+  char challenge[8];
+  struct smb_passwd *smb_pass;
+  BOOL challenge_done = False;
+#endif
+
+  if (password) password[pwlen] = 0;
+
+#ifdef SMB_PASSWD
+  if (pwlen == 24)
+    challenge_done = last_challenge(challenge);
+#endif
+
+#if DEBUG_PASSWORD
+#ifdef SMB_PASSWD
+  if (challenge_done)
+    {
+      int i;      
+      DEBUG(100,("checking user=[%s] pass=[",user));
+      for( i = 0; i < 24; i++)
+       DEBUG(100,("%0x ", (unsigned char)password[i]));
+      DEBUG(100,("]\n"));
+    }
+  else
+#endif
+    DEBUG(100,("checking user=[%s] pass=[%s]\n",user,password));
+#endif
+
+  if (!password)
+    return(False);
+
+  if (((!*password) || (!pwlen)) && !lp_null_passwords())
+    return(False);
+
+  if (pwd && !user) 
+    {
+      pass = (struct passwd *) pwd;
+      user = pass->pw_name;
+    } 
+  else 
+    pass = Get_Pwnam(user,True);
+
+#ifdef SMB_PASSWD
+
+  DEBUG(4,("SMB Password - pwlen = %d, challenge_done = %d\n", pwlen, challenge_done));
+
+  if((pwlen == 24) && challenge_done)
+    {
+      DEBUG(4,("Checking SMB password for user %s (l=24)\n",user));
+
+      if (!pass) 
+       {
+         DEBUG(3,("Couldn't find user %s\n",user));
+         return(False);
+       }
+
+      smb_pass = get_smbpwnam(user);
+      if(!smb_pass)
+       {
+         DEBUG(3,("Couldn't find user %s in smb_passwd file.\n", user));
+         return(False);
+       }
+
+      /* Ensure the uid's match */
+      if(smb_pass->smb_userid != pass->pw_uid)
+       {
+         DEBUG(3,("Error : UNIX and SMB uids in password files do not match !\n"));
+         return(False);
+       }
+
+       if(Protocol >= PROTOCOL_NT1 && is_nt_password)
+       {
+               /* We have the NT MD4 hash challenge available - see if we can
+                  use it (ie. does it exist in the smbpasswd file).
+               */
+               if(smb_pass->smb_nt_passwd != NULL)
+               {
+                 DEBUG(4,("Checking NT MD4 password\n"));
+             if(smb_password_check(password, smb_pass->smb_nt_passwd, challenge))
+                 {
+               update_protected_database(user,True);
+               return(True);
+         }
+                 DEBUG(4,("NT MD4 password check failed\n"));
+                 return (False);
+               }
+       }
+
+       /* Try against the lanman password */
+
+      if(smb_password_check(password, smb_pass->smb_passwd, challenge))
+       {
+         update_protected_database(user,True);
+         return(True);
+       }
+
+       DEBUG(3,("Error smb_password_check failed\n"));
+    }
+#endif 
+
+  DEBUG(4,("Checking password for user %s (l=%d)\n",user,pwlen));
+
+  if (!pass) 
+    {
+      DEBUG(3,("Couldn't find user %s\n",user));
+      return(False);
+    }
+
+#ifdef SHADOW_PWD
+  {
+    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)
+      pass->pw_passwd = spass->sp_pwdp;
+  }
+#endif
+
+#ifdef SecureWare
+  {
+    struct pr_passwd *pr_pw = getprpwnam(pass->pw_name);
+    if (pr_pw && pr_pw->ufld.fd_encrypt)
+      pass->pw_passwd = pr_pw->ufld.fd_encrypt;
+  }
+#endif
+
+#ifdef HPUX_10_TRUSTED
+  {
+    struct pr_passwd *pr_pw = getprpwnam(pass->pw_name);
+    if (pr_pw && pr_pw->ufld.fd_encrypt)
+      pass->pw_passwd = pr_pw->ufld.fd_encrypt;
+  }
+#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 )
+      { 
+       strcpy(pass->pw_name,mypasswd->ufld.fd_name);
+       strcpy(pass->pw_passwd,mypasswd->ufld.fd_encrypt);
+      }
+    else
+      {
+       DEBUG(5,("No entry for user %s in protected database !\n",user));
+       return(False);
+      }
+  }
+#endif
+
+#ifdef ULTRIX_AUTH
+  {
+    AUTHORIZATION *ap = getauthuid( pass->pw_uid );
+    if (ap)
+      {
+       strcpy( pass->pw_passwd, ap->a_password );
+       endauthent();
+      }
+  }
+#endif
+
+  /* extract relevant info */
+  strcpy(this_user,pass->pw_name);  
+  strcpy(this_salt,pass->pw_passwd);
+  strcpy(this_crypted,pass->pw_passwd);
+  if (!*this_crypted) {
+    if (!lp_null_passwords()) {
+      DEBUG(2,("Disallowing access to %s due to null password\n",this_user));
+      return(False);
+    }
+#ifndef PWDAUTH
+    if (!*password) {
+      DEBUG(3,("Allowing access to %s with null password\n",this_user));
+      return(True);
+    }
+#endif    
+  }
+
+  /* try it as it came to us */
+  if (password_check(password))
+    {
+      update_protected_database(user,True);
+      return(True);
+    }
+
+  /* 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(False);
+
+  /* make a copy of it */
+  StrnCpy(pass2,password,sizeof(pstring)-1);
+  
+  /* try all lowercase */
+  strlower(password);
+  if (password_check(password))
+    {
+      update_protected_database(user,True);
+      return(True);
+    }
+
+  /* give up? */
+  if(level < 1)
+    {
+      update_protected_database(user,False);
+
+      /* restore it */
+      strcpy(password,pass2);
+
+      return(False);
+    }
+
+  /* last chance - all combinations of up to level chars upper! */
+  strlower(password);
+
+  if (string_combinations(password,password_check,level))
+    {
+      update_protected_database(user,True);
+      return(True);
+    }
+
+  update_protected_database(user,False);
+  
+  /* restore it */
+  strcpy(password,pass2);
+  
+  return(False);
+}
+
+
+
+/****************************************************************************
+check if a username is valid
+****************************************************************************/
+BOOL user_ok(char *user,int snum)
+{
+  pstring valid, invalid;
+  BOOL ret;
+
+  StrnCpy(valid, lp_valid_users(snum), sizeof(pstring));
+  StrnCpy(invalid, lp_invalid_users(snum), sizeof(pstring));
+
+  string_sub(valid,"%S",lp_servicename(snum));
+  string_sub(invalid,"%S",lp_servicename(snum));
+
+  ret = !user_in_list(user,invalid);
+
+  if (ret && valid && *valid)
+    ret = user_in_list(user,valid);
+
+  if (ret && lp_onlyuser(snum)) {
+    char *user_list = lp_username(snum);
+    string_sub(user_list,"%S",lp_servicename(snum));
+    ret = user_in_list(user,user_list);
+  }
+
+  return(ret);
+}
+
+
+
+
+/****************************************************************************
+validate a group username entry. Return the username or NULL
+****************************************************************************/
+static char *validate_group(char *group,char *password,int pwlen,int snum)
+{
+#ifdef NETGROUP
+  {
+    char *host, *user, *domain;
+    setnetgrent(group);
+    while (getnetgrent(&host, &user, &domain)) {
+      if (user) {
+       if (user_ok(user, snum) && 
+           password_ok(user,password,pwlen,NULL,False)) {
+         endnetgrent();
+         return(user);
+       }
+      }
+    }
+    endnetgrent();
+  }
+#endif
+  
+#if HAVE_GETGRNAM 
+  {
+    struct group *gptr = (struct group *)getgrnam(group);
+    char **member;
+    if (gptr)
+      {
+       member = gptr->gr_mem;
+       while (member && *member)
+         {
+           static fstring name;
+           strcpy(name,*member);
+           if (user_ok(name,snum) &&
+               password_ok(name,password,pwlen,NULL,False))
+             return(&name[0]);
+           member++;
+         }
+#ifdef GROUP_CHECK_PWENT
+       {
+         struct passwd *pwd;
+         static fstring tm;
+         
+         setpwent ();
+         while (pwd = getpwent ()) {
+           if (*(pwd->pw_passwd) && pwd->pw_gid == gptr->gr_gid) {
+             /* This Entry have PASSWORD and same GID then check pwd */
+             if (password_ok(NULL, password, pwlen, pwd,False)) {
+               strcpy(tm, pwd->pw_name);
+               endpwent ();
+               return tm;
+             }
+           }
+         }
+         endpwent ();
+       }
+#endif /* GROUP_CHECK_PWENT */
+      }
+  }      
+#endif
+  return(NULL);
+}
+
+
+
+/****************************************************************************
+check for authority to login to a service with a given username/password
+****************************************************************************/
+BOOL authorise_login(int snum,char *user,char *password, int pwlen, 
+                    BOOL *guest,BOOL *force,int vuid)
+{
+  BOOL ok = False;
+  
+  *guest = False;
+  
+#if DEBUG_PASSWORD
+  DEBUG(100,("checking authorisation on user=%s pass=%s\n",user,password));
+#endif
+
+  /* there are several possabilities:
+     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
+  */
+
+  if (GUEST_ONLY(snum)) *force = True;
+
+  if (!(GUEST_ONLY(snum) && GUEST_OK(snum)))
+    {
+
+      /* check the given username and password */
+      if (!ok && (*user) && user_ok(user,snum)) {
+       ok = password_ok(user,password, pwlen, NULL, False);
+       if (ok) DEBUG(3,("ACCEPTED: given username password ok\n"));
+      }
+
+      /* check for a previously registered guest username */
+      if (!ok && (vuid >= 0) && validated_users[vuid].guest) {   
+       if (user_ok(validated_users[vuid].name,snum) &&
+           password_ok(validated_users[vuid].name, password, pwlen, NULL, False)) {
+         strcpy(user, validated_users[vuid].name);
+         validated_users[vuid].guest = False;
+         DEBUG(3,("ACCEPTED: given password with registered user %s\n", user));
+         ok = True;
+       }
+      }
+
+
+      /* now check the list of session users */
+      if (!ok)
+       {
+         char *auser;
+         char *user_list = strdup(session_users);
+         if (!user_list) return(False);
+
+         for (auser=strtok(user_list,LIST_SEP); 
+              !ok && auser; 
+              auser = strtok(NULL,LIST_SEP))
+           {
+             fstring user2;
+             strcpy(user2,auser);
+             if (!user_ok(user2,snum)) continue;
+                 
+             if (password_ok(user2,password, pwlen, NULL, False)) {
+               ok = True;
+               strcpy(user,user2);
+               DEBUG(3,("ACCEPTED: session list username and given password ok\n"));
+             }
+           }
+         free(user_list);
+       }
+
+      /* check for a previously validated username/password pair */
+      if (!ok && !lp_revalidate(snum) &&
+         (vuid >= 0) && !validated_users[vuid].guest &&
+         user_ok(validated_users[vuid].name,snum)) {
+       strcpy(user,validated_users[vuid].name);
+       *guest = False;
+       DEBUG(3,("ACCEPTED: validated uid ok as non-guest\n"));
+       ok = True;
+      }
+
+      /* check for a rhosts entry */
+      if (!ok && user_ok(user,snum) && check_hosts_equiv(user)) {
+       ok = True;
+       DEBUG(3,("ACCEPTED: hosts equiv or rhosts entry\n"));
+      }
+
+      /* check the user= fields and the given password */
+      if (!ok && lp_username(snum)) {
+       char *auser;
+       pstring user_list;
+       StrnCpy(user_list,lp_username(snum),sizeof(pstring));
+
+       string_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(auser+1,password,pwlen,snum);
+               if (auser)
+                 {
+                   ok = True;
+                   strcpy(user,auser);
+                   DEBUG(3,("ACCEPTED: group username and given password ok\n"));
+                 }
+             }
+           else
+             {
+               fstring user2;
+               strcpy(user2,auser);
+               if (user_ok(user2,snum) && 
+                   password_ok(user2,password,pwlen,NULL, False))
+                 {
+                   ok = True;
+                   strcpy(user,user2);
+                   DEBUG(3,("ACCEPTED: user list username and given password ok\n"));
+                 }
+             }
+         }
+      }      
+    } /* not guest only */
+
+  /* check for a normal guest connection */
+  if (!ok && GUEST_OK(snum))
+    {
+      fstring guestname;
+      StrnCpy(guestname,lp_guestaccount(snum),sizeof(guestname)-1);
+      if (Get_Pwnam(guestname,True))
+       {
+         strcpy(user,guestname);
+         ok = True;
+         DEBUG(3,("ACCEPTED: guest account and guest ok\n"));
+       }
+      else
+       DEBUG(0,("Invalid guest account %s??\n",guestname));
+      *guest = True;
+      *force = True;
+    }
+
+  if (ok && !user_ok(user,snum))
+    {
+      DEBUG(0,("rejected invalid user %s\n",user));
+      ok = False;
+    }
+
+  return(ok);
+}
+
+
+/****************************************************************************
+read the a hosts.equiv or .rhosts file and check if it
+allows this user from this machine
+****************************************************************************/
+static BOOL check_user_equiv(char *user, char *remote, char *equiv_file)
+{
+  pstring buf;
+  int plus_allowed = 1;
+  char *file_host;
+  char *file_user;
+  FILE *fp = fopen(equiv_file, "r");
+  DEBUG(5, ("check_user_equiv %s %s %s\n", user, remote, equiv_file));
+  if (! fp) return False;
+  while(fgets(buf, sizeof(buf), fp)) 
+  {
+    trim_string(buf," "," ");
+
+    if (buf[0] != '#' && buf[0] != '\n') 
+    {
+      BOOL is_group = False;
+      int plus = 1;
+      char *bp = buf;
+      if (strcmp(buf, "NO_PLUS\n") == 0)
+      {
+       DEBUG(6, ("check_user_equiv NO_PLUS\n"));
+       plus_allowed = 0;
+      }
+      else {
+       if (buf[0] == '+') 
+       {
+         bp++;
+         if (*bp == '\n' && plus_allowed) 
+         {
+           /* a bare plus means everbody allowed */
+           DEBUG(6, ("check_user_equiv everybody allowed\n"));
+           fclose(fp);
+           return True;
+         }
+       }
+       else if (buf[0] == '-')
+       {
+         bp++;
+         plus = 0;
+       }
+       if (*bp == '@') 
+       {
+         is_group = True;
+         bp++;
+       }
+       file_host = strtok(bp, " \t\n");
+       file_user = strtok(NULL, " \t\n");
+       DEBUG(7, ("check_user_equiv %s %s\n", file_host, file_user));
+       if (file_host && *file_host) 
+       {
+         BOOL host_ok = False;
+
+#ifdef NETGROUP          
+         /* THIS IS UNTESTED!! */
+         if (is_group)
+           {
+             static char *mydomain = NULL;
+             if (!mydomain)
+               yp_get_default_domain(&mydomain);
+             if (mydomain && innetgr(remote,file_host,user,mydomain))
+               host_ok = True;
+           }
+#else
+         if (is_group)
+           {
+             DEBUG(1,("Netgroups not configured - add -DNETGROUP and recompile\n"));
+             continue;
+           }
+#endif
+
+         /* is it this host */
+         /* the fact that remote has come from a call of gethostbyaddr
+          * means that it may have the fully qualified domain name
+          * so we could look up the file version to get it into
+          * a canonical form, but I would rather just type it
+          * in full in the equiv file
+          */
+         if (!host_ok && !is_group && strequal(remote, file_host))
+           host_ok = True;
+
+         if (!host_ok)
+           continue;
+
+         /* is it this user */
+         if (file_user == 0 || strequal(user, file_user)) 
+           {
+             fclose(fp);
+             DEBUG(5, ("check_user_equiv matched %s%s %s\n",
+                       (plus ? "+" : "-"), file_host,
+                       (file_user ? file_user : "")));
+             return (plus ? True : False);
+           }
+       }
+      }
+    }
+  }
+  fclose(fp);
+  return False;
+}
+
+
+/****************************************************************************
+check for a possible hosts equiv or rhosts entry for the user
+****************************************************************************/
+BOOL check_hosts_equiv(char *user)
+{
+  char *fname = NULL;
+  pstring rhostsfile;
+  struct passwd *pass = Get_Pwnam(user,True);
+
+  extern struct from_host Client_info;
+  extern int Client;
+
+  if (!pass) 
+    return(False);
+
+  fromhost(Client,&Client_info);
+
+  fname = lp_hosts_equiv();
+
+  /* note: don't allow hosts.equiv on root */
+  if (fname && *fname && (pass->pw_uid != 0))
+    {
+      if (check_user_equiv(user,Client_info.name,fname))
+       return(True);
+    }
+  
+  if (lp_use_rhosts())
+    {
+      char *home = get_home_dir(user);
+      if (home)
+       {
+         sprintf(rhostsfile, "%s/.rhosts", home);
+         if (check_user_equiv(user,Client_info.name,rhostsfile))
+           return(True);
+       }
+    }
+
+  return(False);
+}
+
+
+static int password_client = -1;
+static fstring pserver;
+
+/****************************************************************************
+attempted support for server level security 
+****************************************************************************/
+BOOL server_cryptkey(char *buf)
+{
+  pstring inbuf,outbuf;
+  fstring pass_protocol;
+  extern fstring remote_machine;
+  char *p;
+  int len;
+  fstring desthost;
+  struct in_addr dest_ip;
+  extern struct in_addr myip;
+  int port = 139;
+  BOOL ret;
+
+  if (password_client >= 0)
+    close(password_client);
+  password_client = -1;
+
+  if (Protocol < PROTOCOL_NT1) {
+    strcpy(pass_protocol,"LM1.2X002");
+  } else {
+    strcpy(pass_protocol,"NT LM 0.12");
+  }
+
+  bzero(inbuf,sizeof(inbuf));
+  bzero(outbuf,sizeof(outbuf));
+
+  for (p=strtok(lp_passwordserver(),LIST_SEP); p ; p = strtok(NULL,LIST_SEP)) {
+    strcpy(desthost,p);
+    standard_sub_basic(desthost);
+    strupper(desthost);
+
+    dest_ip = *interpret_addr2(desthost);
+    if (zero_ip(dest_ip)) {
+      DEBUG(1,("Can't resolve address for %s\n",p));
+      continue;
+    }
+
+    if (memcmp(&dest_ip,&myip,sizeof(dest_ip)) == 0) {
+      DEBUG(1,("Password server loop - disabling password server %s\n",p));
+      continue;
+    }
+
+    password_client = open_socket_out(SOCK_STREAM, &dest_ip, port);
+    if (password_client >= 0) {
+      DEBUG(3,("connected to password server %s\n",p));
+      StrnCpy(pserver,p,sizeof(pserver)-1);
+      break;
+    }
+  }
+
+  if (password_client < 0) {
+    DEBUG(1,("password server not available\n"));
+    return(False);
+  }
+
+
+  /* send a session request (RFC 8002) */
+
+  /* put in the destination name */
+  len = 4;
+  p = outbuf+len;
+  name_mangle(desthost,p,' ');
+  len += name_len(p);
+
+  /* and my name */
+  p = outbuf+len;
+  name_mangle(remote_machine,p,' ');
+  len += name_len(p);
+
+  _smb_setlen(outbuf,len);
+  CVAL(outbuf,0) = 0x81;
+
+  send_smb(password_client,outbuf);
+  receive_smb(password_client,inbuf,5000);
+  if (CVAL(inbuf,0) != 0x82) {
+    DEBUG(1,("%s rejected the session\n",pserver));
+    close(password_client); password_client = -1;
+    return(False);
+  }
+
+  DEBUG(3,("got session\n"));
+
+  bzero(outbuf,smb_size);
+
+  /* setup the protocol string */
+  set_message(outbuf,0,strlen(pass_protocol)+2,True);
+  p = smb_buf(outbuf);
+  *p++ = 2;
+  strcpy(p,pass_protocol);
+
+  CVAL(outbuf,smb_com) = SMBnegprot;
+  CVAL(outbuf,smb_flg) = 0x8;
+  SSVAL(outbuf,smb_flg2,0x1);
+
+  send_smb(password_client,outbuf);
+  ret = receive_smb(password_client,inbuf,5000);
+
+  if (!ret || CVAL(inbuf,smb_rcls) || SVAL(inbuf,smb_vwv0)) {
+    DEBUG(1,("%s rejected the protocol\n",pserver));
+    close(password_client); password_client= -1;
+    return(False);
+  }
+
+  if (!(CVAL(inbuf,smb_vwv1) & 1)) {
+    DEBUG(1,("%s isn't in user level security mode\n",pserver));
+    close(password_client); password_client= -1;
+    return(False);
+  }
+
+  memcpy(buf,inbuf,smb_len(inbuf)+4);
+
+  DEBUG(3,("password server OK\n"));
+
+  return(True);
+}
+
+/****************************************************************************
+attempted support for server level security 
+****************************************************************************/
+BOOL server_validate(char *buf)
+{
+  pstring inbuf,outbuf;  
+  BOOL ret;
+
+  if (password_client < 0) {
+    DEBUG(1,("%s not connected\n",pserver));
+    return(False);
+  }  
+
+  bzero(inbuf,sizeof(inbuf));
+  memcpy(outbuf,buf,sizeof(outbuf));
+
+  /* send a session setup command */
+  CVAL(outbuf,smb_flg) = 0x8;
+  SSVAL(outbuf,smb_flg2,0x1);
+  CVAL(outbuf,smb_vwv0) = 0xFF;
+
+  set_message(outbuf,smb_numwords(outbuf),smb_buflen(outbuf),False);
+
+  SCVAL(inbuf,smb_rcls,1);
+
+  send_smb(password_client,outbuf);
+  ret = receive_smb(password_client,inbuf,5000);
+
+  if (!ret || CVAL(inbuf,smb_rcls) != 0) {
+    DEBUG(1,("password server %s rejected the password\n",pserver));
+    return(False);
+  }
+
+  /* if logged in as guest then reject */
+  if ((SVAL(inbuf,smb_vwv2) & 1) != 0) {
+    DEBUG(1,("password server %s gave us guest only\n",pserver));
+    return(False);
+  }
+
+  DEBUG(3,("password server %s accepted the password\n",pserver));
+
+#ifndef KEEP_PASSWORD_SERVER_OPEN
+  close(password_client); password_client= -1;
+#endif
+
+  return(True);
+}
+
+
diff --git a/source/smbd/reply.c b/source/smbd/reply.c
new file mode 100644 (file)
index 0000000..b7b5177
--- /dev/null
@@ -0,0 +1,3210 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   Main SMB reply routines
+   Copyright (C) Andrew Tridgell 1992-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.
+*/
+/*
+   This file handles most of the reply_ calls that the server
+   makes to handle specific protocols
+*/
+
+
+#include "includes.h"
+#include "loadparm.h"
+#include "trans2.h"
+
+/* look in server.c for some explanation of these variables */
+extern int Protocol;
+extern int DEBUGLEVEL;
+extern int chain_size;
+extern int maxxmit;
+extern int chain_fnum;
+extern char magic_char;
+extern connection_struct Connections[];
+extern files_struct Files[];
+extern BOOL case_sensitive;
+extern pstring sesssetup_user;
+extern int Client;
+
+/* this macro should always be used to extract an fnum (smb_fid) from
+a packet to ensure chaining works correctly */
+#define GETFNUM(buf,where) (chain_fnum!= -1?chain_fnum:SVAL(buf,where))
+
+
+/****************************************************************************
+  reply to an special message 
+****************************************************************************/
+int reply_special(char *inbuf,char *outbuf)
+{
+  int outsize = 4;
+  int msg_type = CVAL(inbuf,0);
+  int msg_flags = CVAL(inbuf,1);
+  pstring name1,name2;
+  extern fstring remote_machine;
+  extern fstring local_machine;
+  char *p;
+
+  *name1 = *name2 = 0;
+
+  smb_setlen(outbuf,0);
+
+  switch (msg_type)
+    {
+    case 0x81: /* session request */
+      CVAL(outbuf,0) = 0x82;
+      CVAL(outbuf,3) = 0;
+      if (name_len(inbuf+4) > 50)
+       {
+         DEBUG(0,("Invalid name length in session request\n"));
+         return(0);
+       }
+      name_extract(inbuf,4,name1);
+      name_extract(inbuf,4 + name_len(inbuf + 4),name2);
+      DEBUG(2,("netbios connect: name1=%s name2=%s\n",name1,name2));      
+
+      strcpy(remote_machine,name2);
+      trim_string(remote_machine," "," ");
+      p = strchr(remote_machine,' ');
+      strlower(remote_machine);
+      if (p) *p = 0;
+
+      strcpy(local_machine,name1);
+      trim_string(local_machine," "," ");
+      p = strchr(local_machine,' ');
+      strlower(local_machine);
+      if (p) *p = 0;
+
+      add_session_user(remote_machine);
+
+      reload_services(True);
+      reopen_logs();
+
+      break;
+    case 0x85: /* session keepalive */
+    default:
+      return(0);
+    }
+  
+  DEBUG(5,("%s init msg_type=0x%x msg_flags=0x%x\n",timestring(),msg_type,msg_flags));
+  
+  return(outsize);
+}
+
+
+/*******************************************************************
+work out what error to give to a failed connection
+********************************************************************/
+static int connection_error(char *inbuf,char *outbuf,int connection_num)
+{
+  switch (connection_num)
+    {
+    case -8:
+      return(ERROR(ERRSRV,ERRnoresource));
+    case -7:
+      return(ERROR(ERRSRV,ERRbaduid));
+    case -6:
+      return(ERROR(ERRSRV,ERRinvdevice));
+    case -5:
+      return(ERROR(ERRSRV,ERRinvnetname));
+    case -4:
+      return(ERROR(ERRSRV,ERRaccess));
+    case -3:
+      return(ERROR(ERRDOS,ERRnoipc));
+    case -2:
+      return(ERROR(ERRSRV,ERRinvnetname));
+    }
+  return(ERROR(ERRSRV,ERRbadpw));
+}
+
+
+/****************************************************************************
+  reply to a tcon
+****************************************************************************/
+int reply_tcon(char *inbuf,char *outbuf)
+{
+  pstring service;
+  pstring user;
+  pstring password;
+  pstring dev;
+  int connection_num;
+  int outsize = 0;
+  int uid = SVAL(inbuf,smb_uid);
+  int vuid;
+  int pwlen;
+
+  *service = *user = *password = *dev = 0;
+
+  vuid = valid_uid(uid);
+  
+  parse_connect(inbuf,service,user,password,&pwlen,dev);
+
+  connection_num = make_connection(service,user,password,pwlen,dev,vuid);
+  
+  if (connection_num < 0)
+    return(connection_error(inbuf,outbuf,connection_num));
+  
+  outsize = set_message(outbuf,2,0,True);
+  SSVAL(outbuf,smb_vwv0,maxxmit);
+  SSVAL(outbuf,smb_vwv1,connection_num);
+  SSVAL(outbuf,smb_tid,connection_num);
+  
+  DEBUG(3,("%s tcon service=%s user=%s cnum=%d\n",timestring(),service,user,connection_num));
+  
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a tcon and X
+****************************************************************************/
+int reply_tcon_and_X(char *inbuf,char *outbuf,int length,int bufsize)
+{
+  pstring service;
+  pstring user;
+  pstring password;
+  pstring devicename;
+  int connection_num;
+  int outsize = 0;
+  int uid = SVAL(inbuf,smb_uid);
+  int vuid;
+  int smb_com2 = SVAL(inbuf,smb_vwv0);
+  int smb_off2 = SVAL(inbuf,smb_vwv1);
+  int passlen = SVAL(inbuf,smb_vwv3);
+
+  *service = *user = *password = *devicename = 0;
+
+  /* we might have to close an old one */
+  if ((SVAL(inbuf,smb_vwv2) & 0x1) != 0)
+    close_cnum(SVAL(inbuf,smb_tid),uid);
+  
+  vuid = valid_uid(uid);
+  
+  {
+    char *path;
+    char *p;
+    memcpy(password,smb_buf(inbuf),passlen);
+    password[passlen]=0;
+    path = smb_buf(inbuf) + passlen;
+    DEBUG(4,("parsing net-path %s, passlen=%d\n",path,passlen));
+    strcpy(service,path+2);
+    p = strchr(service,'\\');
+    if (!p)
+      return(ERROR(ERRSRV,ERRinvnetname));
+    *p = 0;
+    strcpy(service,p+1);
+    p = strchr(service,'%');
+    if (p)
+      {
+       *p++ = 0;
+       strcpy(user,p);
+      }
+    StrnCpy(devicename,path + strlen(path) + 1,6);
+    DEBUG(4,("Got device type %s\n",devicename));
+  }
+
+  connection_num = make_connection(service,user,password,passlen,devicename,vuid);
+  
+  if (connection_num < 0)
+    return(connection_error(inbuf,outbuf,connection_num));
+
+  outsize = set_message(outbuf,2,strlen(devicename)+1,True);
+  
+  DEBUG(3,("%s tconX service=%s user=%s cnum=%d\n",timestring(),service,user,connection_num));
+  
+  /* set the incoming and outgoing tid to the just created one */
+  SSVAL(inbuf,smb_tid,connection_num);
+  SSVAL(outbuf,smb_tid,connection_num);
+
+  CVAL(outbuf,smb_vwv0) = smb_com2;
+  SSVAL(outbuf,smb_vwv1,(chain_size + outsize)-4);
+
+  strcpy(smb_buf(outbuf),devicename);
+
+  if (smb_com2 != 0xFF)
+    outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4,
+                          outbuf,outbuf+outsize,
+                          length,bufsize);
+
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to an unknown type
+****************************************************************************/
+int reply_unknown(char *inbuf,char *outbuf)
+{
+  int cnum;
+  int type;
+  cnum = SVAL(inbuf,smb_tid);
+  type = CVAL(inbuf,smb_com);
+  
+  DEBUG(0,("%s unknown command type (%s): cnum=%d type=%d (0x%X)\n",
+       timestring(),
+       smb_fn_name(type),
+       cnum,type,type));
+  
+  return(ERROR(ERRSRV,ERRunknownsmb));
+}
+
+
+/****************************************************************************
+  reply to an ioctl
+****************************************************************************/
+int reply_ioctl(char *inbuf,char *outbuf)
+{
+  DEBUG(3,("ignoring ioctl\n"));
+  
+  return(ERROR(ERRSRV,ERRnosupport));
+}
+
+
+/****************************************************************************
+reply to a session setup command
+****************************************************************************/
+int reply_sesssetup_and_X(char *inbuf,char *outbuf,int length,int bufsize)
+{
+  int outsize = 0;
+  int sess_uid;
+  int gid;
+  int   smb_com2;
+  int   smb_off2;       
+  int   smb_bufsize;    
+  int   smb_mpxmax;     
+  int   smb_vc_num;     
+  uint32   smb_sesskey;    
+  int   smb_apasslen;   
+  pstring smb_apasswd;
+  int   smb_ntpasslen = 0;   
+  pstring smb_ntpasswd;
+  BOOL valid_nt_password = False;
+  pstring user;
+  BOOL guest=False;
+
+  *smb_apasswd = 0;
+  
+  sess_uid = SVAL(inbuf,smb_uid);
+  smb_com2 = CVAL(inbuf,smb_vwv0);
+  smb_off2 = SVAL(inbuf,smb_vwv1);
+  smb_bufsize = SVAL(inbuf,smb_vwv2);
+  smb_mpxmax = SVAL(inbuf,smb_vwv3);
+  smb_vc_num = SVAL(inbuf,smb_vwv4);
+  smb_sesskey = IVAL(inbuf,smb_vwv5);
+
+  if (Protocol < PROTOCOL_NT1) {
+    smb_apasslen = SVAL(inbuf,smb_vwv7);
+    memcpy(smb_apasswd,smb_buf(inbuf),smb_apasslen);
+    StrnCpy(user,smb_buf(inbuf)+smb_apasslen,sizeof(user)-1);
+  } else {
+    uint16 passlen1 = SVAL(inbuf,smb_vwv7);
+    uint16 passlen2 = SVAL(inbuf,smb_vwv8);
+    BOOL doencrypt = SMBENCRYPT();
+    char *p = smb_buf(inbuf);
+    if (passlen1 > 256) passlen1 = 0;
+    if (passlen2 > 256) passlen2 = 0; /* I don't know why NT gives weird 
+                                       lengths sometimes */
+    if(doencrypt) {
+      /* Save the lanman2 password and the NT md4 password. */
+      smb_apasslen = passlen1;
+      memcpy(smb_apasswd,p,smb_apasslen);
+      smb_ntpasslen = passlen2;
+      memcpy(smb_ntpasswd,p+passlen1,smb_ntpasslen);
+    } else {
+      /* for Win95 */
+      if (passlen1 > passlen2) {
+       smb_apasslen = passlen1;
+       StrnCpy(smb_apasswd,p,smb_apasslen);
+      } else {
+       smb_apasslen = passlen2;
+       StrnCpy(smb_apasswd,p + passlen1,smb_apasslen);
+      }
+    }
+    if (passlen2 == 1) {
+      /* apparently NT sometimes sets passlen2 to 1 when it means 0. This
+        tries to work around that problem */
+      passlen2 = 0;
+    }
+    p += passlen1 + passlen2;
+    strcpy(user,p); p = skip_string(p,1);
+    DEBUG(3,("Domain=[%s]  NativeOS=[%s] NativeLanMan=[%s]\n",
+            p,skip_string(p,1),skip_string(p,2)));
+  }
+
+
+  DEBUG(3,("sesssetupX:name=[%s]\n",user));
+
+  if (!*user)
+    strcpy(user,lp_guestaccount(-1));
+
+  strlower(user);
+
+  strcpy(sesssetup_user,user);
+
+  reload_services(True);
+
+  add_session_user(user);
+
+
+  if (!(lp_security() == SEC_SERVER && server_validate(inbuf)) &&
+      !check_hosts_equiv(user))
+    {
+
+      if (strequal(user,lp_guestaccount(-1)) && (*smb_apasswd == 0))
+       guest = True;
+  
+      /* now check if it's a valid username/password */
+      /* If an NT password was supplied try and validate with that
+        first. This is superior as the passwords are mixed case 128 length unicode */
+      if(smb_ntpasslen && !guest)
+       {
+         if(!password_ok(user,smb_ntpasswd,smb_ntpasslen,NULL,True))
+           DEBUG(0,("NT Password did not match ! Defaulting to Lanman\n"));
+         else
+           valid_nt_password = True;
+       } 
+      if (!valid_nt_password && !guest && !password_ok(user,smb_apasswd,smb_apasslen,NULL,False))
+       {
+         if (lp_security() >= SEC_USER) {
+#if (GUEST_SESSSETUP == 0)
+           return(ERROR(ERRSRV,ERRbadpw));
+#endif
+#if (GUEST_SESSSETUP == 1)
+           if (Get_Pwnam(user,True))
+             return(ERROR(ERRSRV,ERRbadpw));
+#endif
+         }
+         if (*smb_apasswd || !Get_Pwnam(user,True))
+           strcpy(user,lp_guestaccount(-1));
+         DEBUG(3,("Registered username %s for guest access\n",user));
+         guest = True;
+       }
+    }
+
+  if (!Get_Pwnam(user,True)) {
+    DEBUG(3,("No such user %s - using guest account\n",user));
+    strcpy(user,lp_guestaccount(-1));
+    guest = True;
+  }
+
+  if (!strequal(user,lp_guestaccount(-1)) &&
+      lp_servicenumber(user) < 0)      
+    {
+      int homes = lp_servicenumber(HOMES_NAME);
+      char *home = get_home_dir(user);
+      if (homes >= 0 && home)
+       lp_add_home(user,homes,home);
+    }
+
+
+  /* it's ok - setup a reply */
+  if (Protocol < PROTOCOL_NT1) {
+    outsize = set_message(outbuf,3,0,True);
+  } else {
+    char *p;
+    outsize = set_message(outbuf,3,3,True);
+    p = smb_buf(outbuf);
+    strcpy(p,"Unix"); p = skip_string(p,1);
+    strcpy(p,"Samba "); strcat(p,VERSION); p = skip_string(p,1);
+    strcpy(p,my_workgroup()); p = skip_string(p,1);
+    outsize = set_message(outbuf,3,PTR_DIFF(p,smb_buf(outbuf)),False);
+    /* perhaps grab OS version here?? */
+  }
+
+  /* Set the correct uid in the outgoing and incoming packets
+     We will use this on future requests to determine which
+     user we should become.
+     */
+  {
+    struct passwd *pw = Get_Pwnam(user,False);
+    if (!pw) {
+      DEBUG(1,("Username %s is invalid on this system\n",user));
+      return(ERROR(ERRSRV,ERRbadpw));
+    }
+    gid = pw->pw_gid;
+    SSVAL(outbuf,smb_uid,(uint16)pw->pw_uid);
+    SSVAL(inbuf,smb_uid,(uint16)pw->pw_uid);
+  }
+
+  CVAL(outbuf,smb_vwv0) = smb_com2;
+  SSVAL(outbuf,smb_vwv1,(chain_size+outsize)-4);
+
+  if (guest)
+    SSVAL(outbuf,smb_vwv2,1);
+
+  /* register the name and uid as being validated, so further connections
+     to a uid can get through without a password, on the same VC */
+  register_uid(SVAL(inbuf,smb_uid),gid,user,guest);
+  maxxmit = MIN(maxxmit,smb_bufsize);
+
+  if (smb_com2 != 0xFF)
+    outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4,
+                          outbuf,outbuf+outsize,
+                          length,bufsize);
+
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a chkpth
+****************************************************************************/
+int reply_chkpth(char *inbuf,char *outbuf)
+{
+  int outsize = 0;
+  int cnum,mode;
+  pstring name;
+  BOOL ok = False;
+  
+  cnum = SVAL(inbuf,smb_tid);
+  
+  strcpy(name,smb_buf(inbuf) + 1);
+  unix_convert(name,cnum);
+
+  mode = SVAL(inbuf,smb_vwv0);
+
+  if (check_name(name,cnum))
+    ok = directory_exist(name,NULL);
+
+  if (!ok)
+    return(ERROR(ERRDOS,ERRbadpath));
+  
+  outsize = set_message(outbuf,0,0,True);
+  
+  DEBUG(3,("%s chkpth %s cnum=%d mode=%d\n",timestring(),name,cnum,mode));
+  
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a getatr
+****************************************************************************/
+int reply_getatr(char *inbuf,char *outbuf)
+{
+  pstring fname;
+  int cnum;
+  int outsize = 0;
+  struct stat sbuf;
+  BOOL ok = False;
+  int mode=0;
+  uint32 size=0;
+  time_t mtime=0;
+  
+  cnum = SVAL(inbuf,smb_tid);
+
+  strcpy(fname,smb_buf(inbuf) + 1);
+  unix_convert(fname,cnum);
+
+  /* dos smetimes asks for a stat of "" - it returns a "hidden directory"
+     under WfWg - weird! */
+  if (! (*fname))
+    {
+      mode = aHIDDEN | aDIR;
+      if (!CAN_WRITE(cnum)) mode |= aRONLY;
+      size = 0;
+      mtime = 0;
+      ok = True;
+    }
+  else
+    if (check_name(fname,cnum))
+      {
+       if (sys_stat(fname,&sbuf) == 0)
+         {
+           mode = dos_mode(cnum,fname,&sbuf);
+           size = sbuf.st_size;
+           mtime = sbuf.st_mtime;
+           if (mode & aDIR)
+             size = 0;
+           ok = True;
+         }
+       else
+         DEBUG(3,("stat of %s failed (%s)\n",fname,strerror(errno)));
+    }
+  
+  if (!ok)
+    return(UNIXERROR(ERRDOS,ERRbadfile));
+  
+  outsize = set_message(outbuf,10,0,True);
+
+  SSVAL(outbuf,smb_vwv0,mode);
+  put_dos_date3(outbuf,smb_vwv1,mtime);
+  SIVAL(outbuf,smb_vwv3,size);
+
+  if (Protocol >= PROTOCOL_NT1) {
+    char *p = strrchr(fname,'/');
+    uint16 flg2 = SVAL(outbuf,smb_flg2);
+    if (!p) p = fname;
+    if (!is_8_3(fname))
+      SSVAL(outbuf,smb_flg2,flg2 | 0x40); /* IS_LONG_NAME */
+  }
+  
+  DEBUG(3,("%s getatr name=%s mode=%d size=%d\n",timestring(),fname,mode,size));
+  
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a setatr
+****************************************************************************/
+int reply_setatr(char *inbuf,char *outbuf)
+{
+  pstring fname;
+  int cnum;
+  int outsize = 0;
+  BOOL ok=False;
+  int mode;
+  time_t mtime;
+  
+  cnum = SVAL(inbuf,smb_tid);
+  
+  strcpy(fname,smb_buf(inbuf) + 1);
+  unix_convert(fname,cnum);
+
+  mode = SVAL(inbuf,smb_vwv0);
+  mtime = make_unix_date3(inbuf+smb_vwv1);
+  
+  if (directory_exist(fname,NULL))
+    mode |= aDIR;
+  if (check_name(fname,cnum))
+    ok =  (dos_chmod(cnum,fname,mode,NULL) == 0);
+  if (ok)
+    ok = set_filetime(fname,mtime);
+  
+  if (!ok)
+    return(UNIXERROR(ERRDOS,ERRnoaccess));
+  
+  outsize = set_message(outbuf,0,0,True);
+  
+  DEBUG(3,("%s setatr name=%s mode=%d\n",timestring(),fname,mode));
+  
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a dskattr
+****************************************************************************/
+int reply_dskattr(char *inbuf,char *outbuf)
+{
+  int cnum;
+  int outsize = 0;
+  int dfree,dsize,bsize;
+  
+  cnum = SVAL(inbuf,smb_tid);
+  
+  sys_disk_free(".",&bsize,&dfree,&dsize);
+  
+  outsize = set_message(outbuf,5,0,True);
+  
+  SSVAL(outbuf,smb_vwv0,dsize);
+  SSVAL(outbuf,smb_vwv1,bsize/512);
+  SSVAL(outbuf,smb_vwv2,512);
+  SSVAL(outbuf,smb_vwv3,dfree);
+  
+  DEBUG(3,("%s dskattr cnum=%d dfree=%d\n",timestring(),cnum,dfree));
+  
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a search
+  Can be called from SMBsearch, SMBffirst or SMBfunique.
+****************************************************************************/
+int reply_search(char *inbuf,char *outbuf)
+{
+  pstring mask;
+  pstring directory;
+  pstring fname;
+  int size,mode;
+  time_t date;
+  int dirtype;
+  int cnum;
+  int outsize = 0;
+  int numentries = 0;
+  BOOL finished = False;
+  int maxentries;
+  int i;
+  char *p;
+  BOOL ok = False;
+  int status_len;
+  char *path;
+  char status[21];
+  int dptr_num= -1;
+  BOOL check_descend = False;
+  BOOL expect_close = False;
+  BOOL can_open = True;
+
+  *mask = *directory = *fname = 0;
+
+  /* If we were called as SMBffirst then we must expect close. */
+  if(CVAL(inbuf,smb_com) == SMBffirst)
+    expect_close = True;
+  
+  cnum = SVAL(inbuf,smb_tid);
+
+  outsize = set_message(outbuf,1,3,True);
+  maxentries = SVAL(inbuf,smb_vwv0); 
+  dirtype = SVAL(inbuf,smb_vwv1);
+  path = smb_buf(inbuf) + 1;
+  status_len = SVAL(smb_buf(inbuf),3 + strlen(path));
+
+  
+  /* dirtype &= ~aDIR; */
+  
+  DEBUG(5,("path=%s status_len=%d\n",path,status_len));
+
+  
+  if (status_len == 0)
+    {
+      pstring dir2;
+
+      strcpy(directory,smb_buf(inbuf)+1);
+      strcpy(dir2,smb_buf(inbuf)+1);
+      unix_convert(directory,cnum);
+      unix_format(dir2);
+
+      if (!check_name(directory,cnum))
+       can_open = False;
+
+      p = strrchr(dir2,'/');
+      if (p == NULL) 
+       {strcpy(mask,dir2);*dir2 = 0;}
+      else
+       {*p = 0;strcpy(mask,p+1);}
+
+      p = strrchr(directory,'/');
+      if (!p) 
+       *directory = 0;
+      else
+       *p = 0;
+
+      if (strlen(directory) == 0)
+       strcpy(directory,"./");
+      bzero(status,21);
+      CVAL(status,0) = dirtype;
+    }
+  else
+    {
+      memcpy(status,smb_buf(inbuf) + 1 + strlen(path) + 4,21);
+      memcpy(mask,status+1,11);
+      mask[11] = 0;
+      dirtype = CVAL(status,0) & 0x1F;
+      Connections[cnum].dirptr = dptr_fetch(status+12,&dptr_num);      
+      if (!Connections[cnum].dirptr)
+       goto SearchEmpty;
+      string_set(&Connections[cnum].dirpath,dptr_path(dptr_num));
+      if (!case_sensitive)
+       strnorm(mask);
+    }
+
+  /* turn strings of spaces into a . */  
+  {
+    trim_string(mask,NULL," ");
+    if ((p = strrchr(mask,' ')))
+      {
+       fstring ext;
+       strcpy(ext,p+1);
+       *p = 0;
+       trim_string(mask,NULL," ");
+       strcat(mask,".");
+       strcat(mask,ext);
+      }
+  }
+
+  {
+    for (p=mask; *p; p++)
+      {
+       if (*p != '?' && *p != '*' && !isdoschar(*p))
+         {
+           DEBUG(5,("Invalid char [%c] in search mask?\n",*p));
+           *p = '?';
+         }
+      }
+  }
+
+  if (!strchr(mask,'.') && strlen(mask)>8)
+    {
+      fstring tmp;
+      strcpy(tmp,&mask[8]);
+      mask[8] = '.';
+      mask[9] = 0;
+      strcat(mask,tmp);
+    }
+
+  DEBUG(5,("mask=%s directory=%s\n",mask,directory));
+  
+  if (can_open)
+    {
+      p = smb_buf(outbuf) + 3;
+      
+      ok = True;
+      
+      if (status_len == 0)
+       {
+         dptr_num = dptr_create(cnum,directory,expect_close,SVAL(inbuf,smb_pid));
+         if (dptr_num < 0)
+           return(ERROR(ERRDOS,ERRnofids));
+       }
+
+      DEBUG(4,("dptr_num is %d\n",dptr_num));
+
+      if (ok)
+       {
+         if ((dirtype&0x1F) == aVOLID)
+           {     
+             memcpy(p,status,21);
+             make_dir_struct(p,"???????????",volume_label(SNUM(cnum)),0,aVOLID,0);
+             dptr_fill(p+12,dptr_num);
+             if (dptr_zero(p+12) && (status_len==0))
+               numentries = 1;
+             else
+               numentries = 0;
+             p += DIR_STRUCT_SIZE;
+           }
+         else 
+           {
+             DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n",Connections[cnum].dirpath,lp_dontdescend(SNUM(cnum))));
+             if (in_list(Connections[cnum].dirpath,
+                         lp_dontdescend(SNUM(cnum)),True))
+               check_descend = True;
+
+             for (i=numentries;(i<maxentries) && !finished;i++)
+               {
+                 finished = 
+                   !get_dir_entry(cnum,mask,dirtype,fname,&size,&mode,&date,check_descend);
+                 if (!finished)
+                   {
+                     memcpy(p,status,21);
+                     make_dir_struct(p,mask,fname,size,mode,date);
+                     dptr_fill(p+12,dptr_num);
+                     numentries++;
+                   }
+                 p += DIR_STRUCT_SIZE;
+               }
+           }
+       }
+    }
+
+
+ SearchEmpty:
+
+  if (numentries == 0 || !ok)
+    {
+      CVAL(outbuf,smb_rcls) = ERRDOS;
+      SSVAL(outbuf,smb_err,ERRnofiles);
+    }
+
+  /* If we were called as SMBffirst with smb_search_id == NULL
+     and no entries were found then return error and close dirptr 
+     (X/Open spec) */
+
+  if(ok && expect_close && numentries == 0 && status_len == 0)
+    {
+      CVAL(outbuf,smb_rcls) = ERRDOS;
+      SSVAL(outbuf,smb_err,ERRnofiles);
+      /* Also close the dptr - we know it's gone */
+      dptr_close(dptr_num);
+    }
+
+  /* If we were called as SMBfunique, then we can close the dirptr now ! */
+  if(dptr_num >= 0 && CVAL(inbuf,smb_com) == SMBfunique)
+    dptr_close(dptr_num);
+
+  SSVAL(outbuf,smb_vwv0,numentries);
+  SSVAL(outbuf,smb_vwv1,3 + numentries * DIR_STRUCT_SIZE);
+  CVAL(smb_buf(outbuf),0) = 5;
+  SSVAL(smb_buf(outbuf),1,numentries*DIR_STRUCT_SIZE);
+
+  if (Protocol >= PROTOCOL_NT1) {
+    uint16 flg2 = SVAL(outbuf,smb_flg2);
+    SSVAL(outbuf,smb_flg2,flg2 | 0x40); /* IS_LONG_NAME */
+  }
+  
+  outsize += DIR_STRUCT_SIZE*numentries;
+  smb_setlen(outbuf,outsize - 4);
+  
+  if ((! *directory) && dptr_path(dptr_num))
+    sprintf(directory,"(%s)",dptr_path(dptr_num));
+
+  DEBUG(4,("%s %s mask=%s path=%s cnum=%d dtype=%d nument=%d of %d\n",
+       timestring(),
+       smb_fn_name(CVAL(inbuf,smb_com)), 
+       mask,directory,cnum,dirtype,numentries,maxentries));
+
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a fclose (stop directory search)
+****************************************************************************/
+int reply_fclose(char *inbuf,char *outbuf)
+{
+  int cnum;
+  int outsize = 0;
+  int status_len;
+  char *path;
+  char status[21];
+  int dptr_num= -1;
+
+  cnum = SVAL(inbuf,smb_tid);
+
+  outsize = set_message(outbuf,1,0,True);
+  path = smb_buf(inbuf) + 1;
+  status_len = SVAL(smb_buf(inbuf),3 + strlen(path));
+
+  
+  if (status_len == 0)
+    return(ERROR(ERRSRV,ERRsrverror));
+
+  memcpy(status,smb_buf(inbuf) + 1 + strlen(path) + 4,21);
+
+  if(dptr_fetch(status+12,&dptr_num)) {
+    /*  Close the dptr - we know it's gone */
+    dptr_close(dptr_num);
+  }
+
+  SSVAL(outbuf,smb_vwv0,0);
+
+  DEBUG(3,("%s search close cnum=%d\n",timestring(),cnum));
+
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to an open
+****************************************************************************/
+int reply_open(char *inbuf,char *outbuf)
+{
+  pstring fname;
+  int cnum;
+  int fnum = -1;
+  int outsize = 0;
+  int fmode=0;
+  int share_mode;
+  int size = 0;
+  time_t mtime=0;
+  int unixmode;
+  int rmode=0;
+  struct stat sbuf;
+  
+  cnum = SVAL(inbuf,smb_tid);
+
+  share_mode = SVAL(inbuf,smb_vwv0);
+
+  strcpy(fname,smb_buf(inbuf)+1);
+  unix_convert(fname,cnum);
+    
+  fnum = find_free_file();
+  if (fnum < 0)
+    return(ERROR(ERRSRV,ERRnofids));
+
+  if (!check_name(fname,cnum))
+    return(UNIXERROR(ERRDOS,ERRnoaccess));
+  
+  unixmode = unix_mode(cnum,aARCH);
+      
+  open_file_shared(fnum,cnum,fname,share_mode,3,unixmode,&rmode,NULL);
+
+  if (!Files[fnum].open)
+    return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+  if (fstat(Files[fnum].fd,&sbuf) != 0) {
+    close_file(fnum);
+    return(ERROR(ERRDOS,ERRnoaccess));
+  }
+    
+  size = sbuf.st_size;
+  fmode = dos_mode(cnum,fname,&sbuf);
+  mtime = sbuf.st_mtime;
+
+  if (fmode & aDIR) {
+    DEBUG(3,("attempt to open a directory %s\n",fname));
+    close_file(fnum);
+    return(ERROR(ERRDOS,ERRnoaccess));
+  }
+  
+  outsize = set_message(outbuf,7,0,True);
+  SSVAL(outbuf,smb_vwv0,fnum);
+  SSVAL(outbuf,smb_vwv1,fmode);
+  put_dos_date3(outbuf,smb_vwv2,mtime);
+  SIVAL(outbuf,smb_vwv4,size);
+  SSVAL(outbuf,smb_vwv6,rmode);
+    
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to an open and X
+****************************************************************************/
+int reply_open_and_X(char *inbuf,char *outbuf,int length,int bufsize)
+{
+  pstring fname;
+  int cnum = SVAL(inbuf,smb_tid);
+  int fnum = -1;
+  int outsize = 0;
+  int openmode = 0;
+  int smb_com2 = CVAL(inbuf,smb_vwv0);
+  int smb_off2 = SVAL(inbuf,smb_vwv1);
+  int smb_mode = SVAL(inbuf,smb_vwv3);
+  int smb_attr = SVAL(inbuf,smb_vwv5);
+#if 0
+  int open_flags = SVAL(inbuf,smb_vwv2);
+  int smb_sattr = SVAL(inbuf,smb_vwv4); 
+  uint32 smb_time = make_unix_date3(inbuf+smb_vwv6);
+#endif
+  int smb_ofun = SVAL(inbuf,smb_vwv8);
+  int unixmode;
+  int size=0,fmode=0,mtime=0,rmode=0;
+  struct stat sbuf;
+  int smb_action = 0;
+
+  /* XXXX we need to handle passed times, sattr and flags */
+
+  strcpy(fname,smb_buf(inbuf));
+  unix_convert(fname,cnum);
+    
+  /* now add create and trunc bits */
+  if (smb_ofun & 0x10)
+    openmode |= O_CREAT;
+  if ((smb_ofun & 0x3) == 2)
+    openmode |= O_TRUNC;
+  
+  fnum = find_free_file();
+  if (fnum < 0)
+    return(ERROR(ERRSRV,ERRnofids));
+
+  if (!check_name(fname,cnum))
+    return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+  unixmode = unix_mode(cnum,smb_attr | aARCH);
+      
+  open_file_shared(fnum,cnum,fname,smb_mode,smb_ofun,unixmode,
+                  &rmode,&smb_action);
+      
+  if (!Files[fnum].open)
+    return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+  if (fstat(Files[fnum].fd,&sbuf) != 0) {
+    close_file(fnum);
+    return(ERROR(ERRDOS,ERRnoaccess));
+  }
+
+  size = sbuf.st_size;
+  fmode = dos_mode(cnum,fname,&sbuf);
+  mtime = sbuf.st_mtime;
+  if (fmode & aDIR) {
+    close_file(fnum);
+    return(ERROR(ERRDOS,ERRnoaccess));
+  }
+
+  outsize = set_message(outbuf,15,0,True);
+  CVAL(outbuf,smb_vwv0) = smb_com2;
+  SSVAL(outbuf,smb_vwv1,(chain_size+outsize)-4);
+  SSVAL(outbuf,smb_vwv2,fnum);
+  SSVAL(outbuf,smb_vwv3,fmode);
+  put_dos_date3(outbuf,smb_vwv4,mtime);
+  SIVAL(outbuf,smb_vwv6,size);
+  SSVAL(outbuf,smb_vwv8,rmode);
+  SSVAL(outbuf,smb_vwv11,smb_action);
+
+  chain_fnum = fnum;
+
+  if (smb_com2 != 0xFF)
+    outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4,
+                          outbuf,outbuf+outsize,
+                          length,bufsize);
+
+  chain_fnum = -1;
+  
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a SMBulogoffX
+****************************************************************************/
+int reply_ulogoffX(char *inbuf,char *outbuf,int length,int bufsize)
+{
+  int outsize = 0;
+  int smb_com2 = CVAL(inbuf,smb_vwv0);
+  int smb_off2 = SVAL(inbuf,smb_vwv1);
+  int uid = SVAL(inbuf,smb_uid);
+
+  invalidate_uid(uid);
+
+  outsize = set_message(outbuf,2,0,True);
+  CVAL(outbuf,smb_vwv0) = smb_com2;
+  SSVAL(outbuf,smb_vwv1,(chain_size+outsize)-4);
+
+  DEBUG(3,("%s ulogoffX uid=%d\n",timestring(),uid));
+
+  if (smb_com2 != 0xFF)
+    outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4,
+                          outbuf,outbuf+outsize,
+                          length,bufsize);
+
+  
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a mknew
+****************************************************************************/
+int reply_mknew(char *inbuf,char *outbuf)
+{
+  pstring fname;
+  int cnum,com;
+  int fnum = -1;
+  int outsize = 0;
+  int createmode;
+  mode_t unixmode;
+  
+  com = SVAL(inbuf,smb_com);
+  cnum = SVAL(inbuf,smb_tid);
+
+  createmode = SVAL(inbuf,smb_vwv0);
+  strcpy(fname,smb_buf(inbuf)+1);
+  unix_convert(fname,cnum);
+
+  if (createmode & aVOLID)
+    {
+      DEBUG(0,("Attempt to create file (%s) with volid set - please report this\n",fname));
+    }
+  
+  unixmode = unix_mode(cnum,createmode);
+  
+  if (com == SMBmknew && file_exist(fname,NULL))
+    return(ERROR(ERRDOS,ERRfilexists));
+    
+  fnum = find_free_file();
+  if (fnum < 0)
+    return(ERROR(ERRSRV,ERRnofids));
+
+  if (!check_name(fname,cnum))
+    return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+  open_file(fnum,cnum,fname,O_RDWR | O_CREAT | O_TRUNC,unixmode);
+  
+  if (!Files[fnum].open)
+    return(UNIXERROR(ERRDOS,ERRnoaccess));
+  
+  outsize = set_message(outbuf,1,0,True);
+  SSVAL(outbuf,smb_vwv0,fnum);
+  
+  DEBUG(2,("new file %s\n",fname));
+  DEBUG(3,("%s mknew %s fd=%d fnum=%d cnum=%d dmode=%d umode=%o\n",timestring(),fname,Files[fnum].fd,fnum,cnum,createmode,unixmode));
+  
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a create temporary file
+****************************************************************************/
+int reply_ctemp(char *inbuf,char *outbuf)
+{
+  pstring fname;
+  pstring fname2;
+  int cnum;
+  int fnum = -1;
+  int outsize = 0;
+  int createmode;
+  mode_t unixmode;
+  
+  cnum = SVAL(inbuf,smb_tid);
+  createmode = SVAL(inbuf,smb_vwv0);
+  sprintf(fname,"%s/TMXXXXXX",smb_buf(inbuf)+1);
+  unix_convert(fname,cnum);
+  
+  unixmode = unix_mode(cnum,createmode);
+  
+  fnum = find_free_file();
+  if (fnum < 0)
+    return(ERROR(ERRSRV,ERRnofids));
+
+  if (!check_name(fname,cnum))
+    return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+  strcpy(fname2,(char *)mktemp(fname));
+
+  open_file(fnum,cnum,fname2,O_RDWR | O_CREAT | O_TRUNC,unixmode);
+
+  if (!Files[fnum].open)
+    return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+  outsize = set_message(outbuf,1,2 + strlen(fname2),True);
+  SSVAL(outbuf,smb_vwv0,fnum);
+  CVAL(smb_buf(outbuf),0) = 4;
+  strcpy(smb_buf(outbuf) + 1,fname2);
+  
+  DEBUG(2,("created temp file %s\n",fname2));
+  DEBUG(3,("%s ctemp %s fd=%d fnum=%d cnum=%d dmode=%d umode=%o\n",timestring(),fname2,Files[fnum].fd,fnum,cnum,createmode,unixmode));
+  
+  return(outsize);
+}
+
+
+/*******************************************************************
+check if a user is allowed to delete a file
+********************************************************************/
+static BOOL can_delete(char *fname,int cnum,int dirtype)
+{
+  struct stat sbuf;
+  int fmode;
+
+  if (!CAN_WRITE(cnum)) return(False);
+
+  if (sys_lstat(fname,&sbuf) != 0) return(False);
+  fmode = dos_mode(cnum,fname,&sbuf);
+  if (fmode & aDIR) return(False);
+  if (fmode & aRONLY) return(False);
+  if ((fmode & ~dirtype) & (aHIDDEN | aSYSTEM))
+    return(False);
+  if (!check_file_sharing(cnum,fname)) return(False);
+  return(True);
+}
+
+/****************************************************************************
+  reply to a unlink
+****************************************************************************/
+int reply_unlink(char *inbuf,char *outbuf)
+{
+  int outsize = 0;
+  pstring name;
+  int cnum;
+  int dirtype;
+  pstring directory;
+  pstring mask;
+  char *p;
+  int count=0;
+  int error = ERRnoaccess;
+  BOOL has_wild;
+  BOOL exists=False;
+
+  *directory = *mask = 0;
+
+  cnum = SVAL(inbuf,smb_tid);
+  dirtype = SVAL(inbuf,smb_vwv0);
+  
+  strcpy(name,smb_buf(inbuf) + 1);
+   
+  DEBUG(3,("reply_unlink : %s\n",name));
+   
+  unix_convert(name,cnum);
+
+  p = strrchr(name,'/');
+  if (!p) {
+    strcpy(directory,"./");
+    strcpy(mask,name);
+  } else {
+    *p = 0;
+    strcpy(directory,name);
+    strcpy(mask,p+1);
+  }
+
+  if (is_mangled(mask))
+    check_mangled_stack(mask);
+
+  has_wild = strchr(mask,'*') || strchr(mask,'?');
+
+  if (!has_wild) {
+    strcat(directory,"/");
+    strcat(directory,mask);
+    if (can_delete(directory,cnum,dirtype) && !sys_unlink(directory)) count++;
+    if (!count) exists = file_exist(directory,NULL);    
+  } else {
+    void *dirptr = NULL;
+    char *dname;
+
+    if (check_name(directory,cnum))
+      dirptr = OpenDir(directory);
+
+    if (dirptr)
+      {
+       error = ERRbadfile;
+
+       if (strequal(mask,"????????.???"))
+         strcpy(mask,"*");
+
+       while ((dname = ReadDirName(dirptr)))
+         {
+           pstring fname;
+           strcpy(fname,dname);
+           
+           if(!mask_match(fname, mask, case_sensitive, False)) continue;
+
+           error = ERRnoaccess;
+           sprintf(fname,"%s/%s",directory,dname);
+           if (!can_delete(fname,cnum,dirtype)) continue;
+           if (!sys_unlink(fname)) count++;
+           DEBUG(3,("reply_unlink : doing unlink on %s\n",fname));
+         }
+       CloseDir(dirptr);
+      }
+  }
+  
+  if (count == 0) {
+    if (exists)
+      return(ERROR(ERRDOS,error));
+    else
+      return(UNIXERROR(ERRDOS,error));
+  }
+  
+  outsize = set_message(outbuf,0,0,True);
+  
+  return(outsize);
+}
+
+
+/****************************************************************************
+   reply to a readbraw (core+ protocol)
+****************************************************************************/
+int reply_readbraw(char *inbuf, char *outbuf)
+{
+  int cnum,maxcount,mincount,fnum;
+  int nread = 0;
+  int startpos;
+  char *header = outbuf;
+  int ret=0;
+  int fd;
+  char *fname;
+
+  cnum = SVAL(inbuf,smb_tid);
+  fnum = GETFNUM(inbuf,smb_vwv0);
+
+  startpos = IVAL(inbuf,smb_vwv1);
+  maxcount = SVAL(inbuf,smb_vwv3);
+  mincount = SVAL(inbuf,smb_vwv4);
+
+  /* ensure we don't overrun the packet size */
+  maxcount = MIN(65535,maxcount);
+  maxcount = MAX(mincount,maxcount);
+
+  if (!FNUM_OK(fnum,cnum) || !Files[fnum].can_read)
+    {
+      DEBUG(3,("fnum %d not open in readbraw - cache prime?\n",fnum));
+      _smb_setlen(header,0);
+      transfer_file(0,Client,0,header,4,0);
+      return(-1);
+    }
+  else
+    {
+      fd = Files[fnum].fd;
+      fname = Files[fnum].name;
+    }
+
+
+  if (!is_locked(fnum,cnum,maxcount,startpos))
+    {
+      int size = Files[fnum].size;
+      int sizeneeded = startpos + maxcount;
+           
+      if (size < sizeneeded) {
+       struct stat st;
+       if (fstat(Files[fnum].fd,&st) == 0)
+         size = st.st_size;
+       if (!Files[fnum].can_write) 
+         Files[fnum].size = size;
+      }
+
+      nread = MIN(maxcount,size - startpos);     
+    }
+
+  if (nread < mincount)
+    nread = 0;
+  
+  DEBUG(3,("%s readbraw fnum=%d cnum=%d start=%d max=%d min=%d nread=%d\n",
+          timestring(),
+          fnum,cnum,startpos,
+          maxcount,mincount,nread));
+  
+#if UNSAFE_READRAW
+  {
+    int predict=0;
+    _smb_setlen(header,nread);
+
+    if (!Files[fnum].can_write)
+      predict = read_predict(fd,startpos,header+4,NULL,nread);
+
+    if ((nread-predict) > 0)
+      seek_file(fnum,startpos + predict);
+    
+    ret = transfer_file(fd,Client,nread-predict,header,4+predict,
+                       startpos+predict);
+  }
+
+  if (ret != nread+4)
+    DEBUG(0,("ERROR: file read failure on %s at %d for %d bytes (%d)\n",
+            fname,startpos,nread,ret));
+
+#else
+  ret = read_file(fnum,header+4,startpos,nread,nread,-1,False);
+  if (ret < mincount) ret = 0;
+
+  _smb_setlen(header,ret);
+  transfer_file(0,Client,0,header,4+ret,0);
+#endif
+
+  DEBUG(5,("readbraw finished\n"));
+  return -1;
+}
+
+
+/****************************************************************************
+  reply to a lockread (core+ protocol)
+****************************************************************************/
+int reply_lockread(char *inbuf,char *outbuf)
+{
+  int cnum,fnum;
+  int nread = -1;
+  char *data;
+  int outsize = 0;
+  uint32 startpos, numtoread;
+  int eclass;
+  uint32 ecode;
+  
+  cnum = SVAL(inbuf,smb_tid);
+  fnum = GETFNUM(inbuf,smb_vwv0);
+
+  CHECK_FNUM(fnum,cnum);
+  CHECK_READ(fnum);
+  CHECK_ERROR(fnum);
+
+  numtoread = SVAL(inbuf,smb_vwv1);
+  startpos = IVAL(inbuf,smb_vwv2);
+  
+  outsize = set_message(outbuf,5,3,True);
+  numtoread = MIN(BUFFER_SIZE-outsize,numtoread);
+  data = smb_buf(outbuf) + 3;
+  
+  if(!do_lock( fnum, cnum, numtoread, startpos, &eclass, &ecode))
+    return (ERROR(eclass,ecode));
+
+  nread = read_file(fnum,data,startpos,numtoread,numtoread,-1,False);
+  
+  if (nread < 0)
+    return(UNIXERROR(ERRDOS,ERRnoaccess));
+  
+  outsize += nread;
+  SSVAL(outbuf,smb_vwv0,nread);
+  SSVAL(outbuf,smb_vwv5,nread+3);
+  SSVAL(smb_buf(outbuf),1,nread);
+  
+  DEBUG(3,("%s lockread fnum=%d cnum=%d num=%d nread=%d\n",timestring(),fnum,cnum,numtoread,nread));
+  
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a read
+****************************************************************************/
+int reply_read(char *inbuf,char *outbuf)
+{
+  int cnum,numtoread,fnum;
+  int nread = 0;
+  char *data;
+  int startpos;
+  int outsize = 0;
+  
+  cnum = SVAL(inbuf,smb_tid);
+  fnum = GETFNUM(inbuf,smb_vwv0);
+
+  CHECK_FNUM(fnum,cnum);
+  CHECK_READ(fnum);
+  CHECK_ERROR(fnum);
+
+  numtoread = SVAL(inbuf,smb_vwv1);
+  startpos = IVAL(inbuf,smb_vwv2);
+  
+  outsize = set_message(outbuf,5,3,True);
+  numtoread = MIN(BUFFER_SIZE-outsize,numtoread);
+  data = smb_buf(outbuf) + 3;
+  
+  if (is_locked(fnum,cnum,numtoread,startpos))
+    return(ERROR(ERRDOS,ERRlock));     
+
+  if (numtoread > 0)
+    nread = read_file(fnum,data,startpos,numtoread,numtoread,-1,False);
+  
+  if (nread < 0)
+    return(UNIXERROR(ERRDOS,ERRnoaccess));
+  
+  outsize += nread;
+  SSVAL(outbuf,smb_vwv0,nread);
+  SSVAL(outbuf,smb_vwv5,nread+3);
+  CVAL(smb_buf(outbuf),0) = 1;
+  SSVAL(smb_buf(outbuf),1,nread);
+  
+  DEBUG(3,("%s read fnum=%d cnum=%d num=%d nread=%d\n",timestring(),fnum,cnum,numtoread,nread));
+  
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a read and X
+****************************************************************************/
+int reply_read_and_X(char *inbuf,char *outbuf,int length,int bufsize)
+{
+  int smb_com2 = CVAL(inbuf,smb_vwv0);
+  int smb_off2 = SVAL(inbuf,smb_vwv1);
+  int fnum = GETFNUM(inbuf,smb_vwv2);
+  uint32 smb_offs = IVAL(inbuf,smb_vwv3);
+  int smb_maxcnt = SVAL(inbuf,smb_vwv5);
+  int smb_mincnt = SVAL(inbuf,smb_vwv6);
+  int cnum;
+  int nread = -1;
+  char *data;
+  int outsize = 0;
+  BOOL ok = False;
+
+  cnum = SVAL(inbuf,smb_tid);
+
+  CHECK_FNUM(fnum,cnum);
+  CHECK_READ(fnum);
+  CHECK_ERROR(fnum);
+
+  outsize = set_message(outbuf,12,0,True);
+  data = smb_buf(outbuf);
+
+  if (is_locked(fnum,cnum,smb_maxcnt,smb_offs))
+    return(ERROR(ERRDOS,ERRlock));
+  nread = read_file(fnum,data,smb_offs,smb_maxcnt,smb_maxcnt,-1,False);
+  ok = True;
+  
+  if (nread < 0)
+    return(UNIXERROR(ERRDOS,ERRnoaccess));
+  
+  outsize += nread;
+  CVAL(outbuf,smb_vwv0) = smb_com2;
+  SSVAL(outbuf,smb_vwv1,(outsize+chain_size)-4);
+  SSVAL(outbuf,smb_vwv5,nread);
+  SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf) + chain_size);
+  SSVAL(smb_buf(outbuf),-2,nread);
+  
+  DEBUG(3,("%s readX fnum=%d cnum=%d min=%d max=%d nread=%d com2=%d off2=%d\n",
+       timestring(),fnum,cnum,
+       smb_mincnt,smb_maxcnt,nread,smb_com2,smb_off2));
+
+  chain_fnum = fnum;
+
+  if (smb_com2 != 0xFF)
+    outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4,
+                          outbuf,outbuf+outsize,
+                          length,bufsize);
+  
+  chain_fnum = -1;
+  
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a writebraw (core+ or LANMAN1.0 protocol)
+****************************************************************************/
+int reply_writebraw(char *inbuf,char *outbuf)
+{
+  int nwritten=0;
+  int total_written=0;
+  int numtowrite=0;
+  int cnum,fnum;
+  int outsize = 0;
+  long startpos;
+  char *data=NULL;
+  BOOL write_through;
+  int tcount;
+
+  cnum = SVAL(inbuf,smb_tid);
+  fnum = GETFNUM(inbuf,smb_vwv0);
+
+  CHECK_FNUM(fnum,cnum);
+  CHECK_WRITE(fnum);
+  CHECK_ERROR(fnum);
+  
+  tcount = IVAL(inbuf,smb_vwv1);
+  startpos = IVAL(inbuf,smb_vwv3);
+  write_through = BITSETW(inbuf+smb_vwv7,0);
+
+  /* We have to deal with slightly different formats depending
+     on whether we are using the core+ or lanman1.0 protocol */
+  if(Protocol <= PROTOCOL_COREPLUS) {
+    numtowrite = SVAL(smb_buf(inbuf),-2);
+    data = smb_buf(inbuf);
+  } else {
+    numtowrite = SVAL(inbuf,smb_vwv10);
+    data = smb_base(inbuf) + SVAL(inbuf, smb_vwv11);
+  }
+
+  /* force the error type */
+  CVAL(inbuf,smb_com) = SMBwritec;
+  CVAL(outbuf,smb_com) = SMBwritec;
+
+  if (is_locked(fnum,cnum,tcount,startpos))
+    return(ERROR(ERRDOS,ERRlock));
+
+  if (seek_file(fnum,startpos) != startpos)
+    DEBUG(0,("couldn't seek to %d in writebraw\n",startpos));
+
+  if (numtowrite>0)
+    nwritten = write_file(fnum,data,numtowrite);
+  
+  DEBUG(3,("%s writebraw1 fnum=%d cnum=%d start=%d num=%d wrote=%d sync=%d\n",
+          timestring(),fnum,cnum,startpos,numtowrite,nwritten,write_through));
+
+  if (nwritten < numtowrite) 
+    return(UNIXERROR(ERRHRD,ERRdiskfull));
+
+  total_written = nwritten;
+
+  /* Return a message to the redirector to tell it
+     to send more bytes */
+  CVAL(outbuf,smb_com) = SMBwritebraw;
+  SSVALS(outbuf,smb_vwv0,-1);
+  outsize = set_message(outbuf,Protocol>PROTOCOL_COREPLUS?1:0,0,True);
+  send_smb(Client,outbuf);
+  
+  /* Now read the raw data into the buffer and write it */
+  if(read_smb_length(Client,inbuf,0) == -1) {
+    exit_server("secondary writebraw failed");
+  }
+  
+  /* Even though this is not an smb message, smb_len
+     returns the generic length of an smb message */
+  numtowrite = smb_len(inbuf);
+
+  if (tcount > nwritten+numtowrite) {
+    DEBUG(3,("Client overestimated the write %d %d %d\n",
+            tcount,nwritten,numtowrite));
+  }
+
+  nwritten = transfer_file(Client,Files[fnum].fd,numtowrite,NULL,0,
+                          startpos+nwritten);
+  total_written += nwritten;
+  
+  /* Set up outbuf to return the correct return */
+  outsize = set_message(outbuf,1,0,True);
+  CVAL(outbuf,smb_com) = SMBwritec;
+  SSVAL(outbuf,smb_vwv0,total_written);
+
+  if (nwritten < numtowrite) {
+    CVAL(outbuf,smb_rcls) = ERRHRD;
+    SSVAL(outbuf,smb_err,ERRdiskfull);      
+  }
+
+  if (lp_syncalways(SNUM(cnum)) || write_through)
+    sync_file(fnum);
+
+  DEBUG(3,("%s writebraw2 fnum=%d cnum=%d start=%d num=%d wrote=%d\n",
+          timestring(),fnum,cnum,startpos,numtowrite,total_written));
+
+  /* we won't return a status if write through is not selected - this 
+     follows what WfWg does */
+  if (!write_through && total_written==tcount)
+    return(-1);
+
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a writeunlock (core+)
+****************************************************************************/
+int reply_writeunlock(char *inbuf,char *outbuf)
+{
+  int cnum,fnum;
+  int nwritten = -1;
+  int outsize = 0;
+  char *data;
+  uint32 numtowrite,startpos;
+  int eclass;
+  uint32 ecode;
+  
+  cnum = SVAL(inbuf,smb_tid);
+  fnum = GETFNUM(inbuf,smb_vwv0);
+
+  CHECK_FNUM(fnum,cnum);
+  CHECK_WRITE(fnum);
+  CHECK_ERROR(fnum);
+
+  numtowrite = SVAL(inbuf,smb_vwv1);
+  startpos = IVAL(inbuf,smb_vwv2);
+  data = smb_buf(inbuf) + 3;
+  
+  if (is_locked(fnum,cnum,numtowrite,startpos))
+    return(ERROR(ERRDOS,ERRlock));
+
+  seek_file(fnum,startpos);
+
+  /* The special X/Open SMB protocol handling of
+     zero length writes is *NOT* done for
+     this call */
+  if(numtowrite == 0)
+    nwritten = 0;
+  else
+    nwritten = write_file(fnum,data,numtowrite);
+  
+  if (lp_syncalways(SNUM(cnum)))
+    sync_file(fnum);
+
+  if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0))
+    return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+  if(!do_unlock(fnum, cnum, numtowrite, startpos, &eclass, &ecode))
+    return(ERROR(eclass,ecode));
+
+  outsize = set_message(outbuf,1,0,True);
+  
+  SSVAL(outbuf,smb_vwv0,nwritten);
+  
+  DEBUG(3,("%s writeunlock fnum=%d cnum=%d num=%d wrote=%d\n",
+          timestring(),fnum,cnum,numtowrite,nwritten));
+  
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a write
+****************************************************************************/
+int reply_write(char *inbuf,char *outbuf,int dum1,int dum2)
+{
+  int cnum,numtowrite,fnum;
+  int nwritten = -1;
+  int outsize = 0;
+  int startpos;
+  char *data;
+
+  dum1 = dum2 = 0;
+
+  
+  cnum = SVAL(inbuf,smb_tid);
+  fnum = GETFNUM(inbuf,smb_vwv0);
+
+  CHECK_FNUM(fnum,cnum);
+  CHECK_WRITE(fnum);
+  CHECK_ERROR(fnum);
+
+  numtowrite = SVAL(inbuf,smb_vwv1);
+  startpos = IVAL(inbuf,smb_vwv2);
+  data = smb_buf(inbuf) + 3;
+  
+  if (is_locked(fnum,cnum,numtowrite,startpos))
+    return(ERROR(ERRDOS,ERRlock));
+
+  seek_file(fnum,startpos);
+
+  /* X/Open SMB protocol says that if smb_vwv1 is
+     zero then the file size should be extended or
+     truncated to the size given in smb_vwv[2-3] */
+  if(numtowrite == 0)
+    nwritten = set_filelen(Files[fnum].fd, startpos);
+  else
+    nwritten = write_file(fnum,data,numtowrite);
+  
+  if (lp_syncalways(SNUM(cnum)))
+    sync_file(fnum);
+
+  if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0))
+    return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+  outsize = set_message(outbuf,1,0,True);
+  
+  SSVAL(outbuf,smb_vwv0,nwritten);
+
+  if (nwritten < numtowrite) {
+    CVAL(outbuf,smb_rcls) = ERRHRD;
+    SSVAL(outbuf,smb_err,ERRdiskfull);      
+  }
+  
+  DEBUG(3,("%s write fnum=%d cnum=%d num=%d wrote=%d\n",timestring(),fnum,cnum,numtowrite,nwritten));
+  
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a write and X
+****************************************************************************/
+int reply_write_and_X(char *inbuf,char *outbuf,int length,int bufsize)
+{
+  int smb_com2 = CVAL(inbuf,smb_vwv0);
+  int smb_off2 = SVAL(inbuf,smb_vwv1);
+  int fnum = GETFNUM(inbuf,smb_vwv2);
+  uint32 smb_offs = IVAL(inbuf,smb_vwv3);
+  int smb_dsize = SVAL(inbuf,smb_vwv10);
+  int smb_doff = SVAL(inbuf,smb_vwv11);
+  BOOL write_through = BITSETW(inbuf+smb_vwv7,0);
+  int cnum;
+  int nwritten = -1;
+  int outsize = 0;
+  char *data;
+
+  cnum = SVAL(inbuf,smb_tid);
+
+  CHECK_FNUM(fnum,cnum);
+  CHECK_WRITE(fnum);
+  CHECK_ERROR(fnum);
+
+  data = smb_base(inbuf) + smb_doff;
+
+  if (is_locked(fnum,cnum,smb_dsize,smb_offs))
+    return(ERROR(ERRDOS,ERRlock));
+
+  seek_file(fnum,smb_offs);
+  
+  /* X/Open SMB protocol says that, unlike SMBwrite
+     if the length is zero then NO truncation is
+     done, just a write of zero. To truncate a file,
+     use SMBwrite. */
+  if(smb_dsize == 0)
+    nwritten = 0;
+  else
+    nwritten = write_file(fnum,data,smb_dsize);
+  
+  if(((nwritten == 0) && (smb_dsize != 0))||(nwritten < 0))
+    return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+  outsize = set_message(outbuf,6,0,True);
+  
+  CVAL(outbuf,smb_vwv0) = smb_com2;
+  SSVAL(outbuf,smb_vwv1,(outsize+chain_size)-4);
+  SSVAL(outbuf,smb_vwv2,nwritten);
+  
+  if (nwritten < smb_dsize) {
+    CVAL(outbuf,smb_rcls) = ERRHRD;
+    SSVAL(outbuf,smb_err,ERRdiskfull);      
+  }
+
+  DEBUG(3,("%s writeX fnum=%d cnum=%d num=%d wrote=%d\n",timestring(),fnum,cnum,smb_dsize,nwritten));
+
+  chain_fnum = fnum;
+
+  if (lp_syncalways(SNUM(cnum)) || write_through)
+    sync_file(fnum);
+
+  if (smb_com2 != 0xFF)
+    outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4,
+                          outbuf,outbuf+outsize,
+                          length,bufsize);
+  
+  chain_fnum = -1;
+  
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a lseek
+****************************************************************************/
+int reply_lseek(char *inbuf,char *outbuf)
+{
+  int cnum,fnum;
+  uint32 startpos;
+  int32 res= -1;
+  int mode,umode;
+  int outsize = 0;
+  
+  cnum = SVAL(inbuf,smb_tid);
+  fnum = GETFNUM(inbuf,smb_vwv0);
+
+  CHECK_FNUM(fnum,cnum);
+  CHECK_ERROR(fnum);
+
+  mode = SVAL(inbuf,smb_vwv1) & 3;
+  startpos = IVAL(inbuf,smb_vwv2);
+
+  switch (mode & 3) 
+    {
+    case 0: umode = SEEK_SET; break;
+    case 1: umode = SEEK_CUR; break;
+    case 2: umode = SEEK_END; break;
+    default:
+      umode = SEEK_SET; break;
+    }
+  
+  res = lseek(Files[fnum].fd,startpos,umode);
+  Files[fnum].pos = res;
+  
+  outsize = set_message(outbuf,2,0,True);
+  SIVALS(outbuf,smb_vwv0,res);
+  
+  DEBUG(3,("%s lseek fnum=%d cnum=%d ofs=%d mode=%d\n",timestring(),fnum,cnum,startpos,mode));
+  
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a flush
+****************************************************************************/
+int reply_flush(char *inbuf,char *outbuf)
+{
+  int cnum, fnum;
+  int outsize = set_message(outbuf,0,0,True);
+
+  cnum = SVAL(inbuf,smb_tid);
+  fnum = GETFNUM(inbuf,smb_vwv0);
+
+  if (fnum != 0xFFFF) {
+    CHECK_FNUM(fnum,cnum);
+    CHECK_ERROR(fnum);
+  }
+
+  if (fnum == 0xFFFF)
+    {
+      int i;
+      for (i=0;i<MAX_OPEN_FILES;i++)
+       if (OPEN_FNUM(i))
+         sync_file(i);
+    }
+  else
+    sync_file(fnum);
+
+  DEBUG(3,("%s flush fnum=%d\n",timestring(),fnum));
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a exit
+****************************************************************************/
+int reply_exit(char *inbuf,char *outbuf)
+{
+  int outsize = set_message(outbuf,0,0,True);
+  DEBUG(3,("%s exit\n",timestring()));
+  
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a close
+****************************************************************************/
+int reply_close(char *inbuf,char *outbuf)
+{
+  int fnum,cnum;
+  int outsize = 0;
+  time_t mtime;
+  int32 eclass = 0, err = 0;
+
+  outsize = set_message(outbuf,0,0,True);
+
+  cnum = SVAL(inbuf,smb_tid);
+
+  fnum = GETFNUM(inbuf,smb_vwv0);
+  CHECK_FNUM(fnum,cnum);
+
+  if(HAS_CACHED_ERROR(fnum)) {
+    eclass = Files[fnum].wbmpx_ptr->wr_errclass;
+    err = Files[fnum].wbmpx_ptr->wr_error;
+  }
+
+  mtime = make_unix_date3(inbuf+smb_vwv1);
+
+  close_file(fnum);
+
+  /* try and set the date */
+  set_filetime(Files[fnum].name,mtime);
+
+  /* We have a cached error */
+  if(eclass || err)
+    return(ERROR(eclass,err));
+
+  DEBUG(3,("%s close fd=%d fnum=%d cnum=%d (numopen=%d)\n",
+          timestring(),Files[fnum].fd,fnum,cnum,
+          Connections[cnum].num_files_open));
+  
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a writeclose (Core+ protocol)
+****************************************************************************/
+int reply_writeclose(char *inbuf,char *outbuf)
+{
+  int cnum,numtowrite,fnum;
+  int nwritten = -1;
+  int outsize = 0;
+  int startpos;
+  char *data;
+  time_t mtime;
+  
+  cnum = SVAL(inbuf,smb_tid);
+  fnum = GETFNUM(inbuf,smb_vwv0);
+
+  CHECK_FNUM(fnum,cnum);
+  CHECK_WRITE(fnum);
+  CHECK_ERROR(fnum);
+
+  numtowrite = SVAL(inbuf,smb_vwv1);
+  startpos = IVAL(inbuf,smb_vwv2);
+  mtime = make_unix_date3(inbuf+smb_vwv4);
+  data = smb_buf(inbuf) + 1;
+  
+  if (is_locked(fnum,cnum,numtowrite,startpos))
+    return(ERROR(ERRDOS,ERRlock));
+      
+  seek_file(fnum,startpos);
+      
+  nwritten = write_file(fnum,data,numtowrite);
+
+  close_file(fnum);
+
+  set_filetime(Files[fnum].name,mtime);
+  
+  DEBUG(3,("%s writeclose fnum=%d cnum=%d num=%d wrote=%d (numopen=%d)\n",
+          timestring(),fnum,cnum,numtowrite,nwritten,
+          Connections[cnum].num_files_open));
+  
+  if (nwritten <= 0)
+    return(UNIXERROR(ERRDOS,ERRnoaccess));
+  
+  outsize = set_message(outbuf,1,0,True);
+  
+  SSVAL(outbuf,smb_vwv0,nwritten);
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a lock
+****************************************************************************/
+int reply_lock(char *inbuf,char *outbuf)
+{
+  int fnum,cnum;
+  int outsize = set_message(outbuf,0,0,True);
+  uint32 count,offset;
+  int eclass;
+  uint32 ecode;
+
+  cnum = SVAL(inbuf,smb_tid);
+  fnum = GETFNUM(inbuf,smb_vwv0);
+
+  CHECK_FNUM(fnum,cnum);
+  CHECK_ERROR(fnum);
+
+  count = IVAL(inbuf,smb_vwv1);
+  offset = IVAL(inbuf,smb_vwv3);
+
+  DEBUG(3,("%s lock fd=%d fnum=%d cnum=%d ofs=%d cnt=%d\n",timestring(),Files[fnum].fd,fnum,cnum,offset,count));
+
+  if(!do_lock( fnum, cnum, count, offset, &eclass, &ecode))
+    return (ERROR(eclass,ecode));
+  
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a unlock
+****************************************************************************/
+int reply_unlock(char *inbuf,char *outbuf)
+{
+  int fnum,cnum;
+  int outsize = set_message(outbuf,0,0,True);
+  uint32 count,offset;
+  int eclass;
+  uint32 ecode;
+  
+  cnum = SVAL(inbuf,smb_tid);
+  fnum = GETFNUM(inbuf,smb_vwv0);
+
+  CHECK_FNUM(fnum,cnum);
+  CHECK_ERROR(fnum);
+
+  count = IVAL(inbuf,smb_vwv1);
+  offset = IVAL(inbuf,smb_vwv3);
+
+  if(!do_unlock(fnum, cnum, count, offset, &eclass, &ecode))
+    return (ERROR(eclass,ecode));
+
+  DEBUG(3,("%s unlock fd=%d fnum=%d cnum=%d ofs=%d cnt=%d\n",timestring(),Files[fnum].fd,fnum,cnum,offset,count));
+  
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a tdis
+****************************************************************************/
+int reply_tdis(char *inbuf,char *outbuf)
+{
+  int cnum, uid;
+  int outsize = set_message(outbuf,0,0,True);
+  
+  cnum = SVAL(inbuf,smb_tid);
+  uid = SVAL(inbuf,smb_uid);
+
+  Connections[cnum].used = False;
+
+  close_cnum(cnum,uid);
+  
+  DEBUG(3,("%s tdis cnum=%d\n",timestring(),cnum));
+
+  return outsize;
+}
+
+
+
+/****************************************************************************
+  reply to a echo
+****************************************************************************/
+int reply_echo(char *inbuf,char *outbuf)
+{
+  int cnum;
+  int smb_reverb = SVAL(inbuf,smb_vwv0);
+  int seq_num;
+  int data_len = smb_buflen(inbuf);
+  int outsize = set_message(outbuf,1,data_len,True);
+
+  cnum = SVAL(inbuf,smb_tid);
+
+  if (cnum != 0xFFFF && !OPEN_CNUM(cnum))
+    {
+      DEBUG(4,("Invalid cnum in echo (%d)\n",cnum));
+      return(ERROR(ERRSRV,ERRinvnid));
+    }
+
+  /* copy any incoming data back out */
+  if (data_len > 0)
+    memcpy(smb_buf(outbuf),smb_buf(inbuf),data_len);
+
+  if (smb_reverb > 100)
+    {
+      DEBUG(0,("large reverb (%d)?? Setting to 100\n",smb_reverb));
+      smb_reverb = 100;
+    }
+
+  for (seq_num =1 ; seq_num <= smb_reverb ; seq_num++)
+    {
+      SSVAL(outbuf,smb_vwv0,seq_num);
+
+      smb_setlen(outbuf,outsize - 4);
+
+      send_smb(Client,outbuf);
+    }
+
+  DEBUG(3,("%s echo %d times cnum=%d\n",timestring(),smb_reverb,cnum));
+
+  return -1;
+}
+
+
+/****************************************************************************
+  reply to a printopen
+****************************************************************************/
+int reply_printopen(char *inbuf,char *outbuf)
+{
+  pstring fname;
+  pstring fname2;
+  int cnum;
+  int fnum = -1;
+  int outsize = 0;
+
+  *fname = *fname2 = 0;
+
+  cnum = SVAL(inbuf,smb_tid);
+
+  if (!CAN_PRINT(cnum))
+    return(ERROR(ERRDOS,ERRnoaccess));
+
+  {
+    pstring s;
+    char *p;
+    StrnCpy(s,smb_buf(inbuf)+1,sizeof(pstring)-1);
+    p = s;
+    while (*p)
+      {
+       if (!(isalnum(*p) || strchr("._-",*p)))
+         *p = 'X';
+       p++;
+      }
+
+    if (strlen(s) > 10) s[10] = 0;
+
+    sprintf(fname,"%s.XXXXXX",s);  
+  }
+
+  fnum = find_free_file();
+  if (fnum < 0)
+    return(ERROR(ERRSRV,ERRnofids));
+
+  strcpy(fname2,(char *)mktemp(fname));
+
+  if (!check_name(fname2,cnum))
+    return(ERROR(ERRDOS,ERRnoaccess));
+
+  open_file(fnum,cnum,fname2,O_WRONLY | O_CREAT | O_TRUNC,
+           unix_mode(cnum,0));
+
+  if (!Files[fnum].open)
+    return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+  /* force it to be a print file */
+  Files[fnum].print_file = True;
+  
+  outsize = set_message(outbuf,1,0,True);
+  SSVAL(outbuf,smb_vwv0,fnum);
+  
+  DEBUG(3,("%s openprint %s fd=%d fnum=%d cnum=%d\n",timestring(),fname2,Files[fnum].fd,fnum,cnum));
+  
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a printclose
+****************************************************************************/
+int reply_printclose(char *inbuf,char *outbuf)
+{
+  int fnum,cnum;
+  int outsize = set_message(outbuf,0,0,True);
+  
+  cnum = SVAL(inbuf,smb_tid);
+  fnum = GETFNUM(inbuf,smb_vwv0);
+
+  CHECK_FNUM(fnum,cnum);
+  CHECK_ERROR(fnum);
+
+  if (!CAN_PRINT(cnum))
+    return(ERROR(ERRDOS,ERRnoaccess));
+  
+  close_file(fnum);
+  
+  DEBUG(3,("%s printclose fd=%d fnum=%d cnum=%d\n",timestring(),Files[fnum].fd,fnum,cnum));
+  
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a printqueue
+****************************************************************************/
+int reply_printqueue(char *inbuf,char *outbuf)
+{
+  int cnum, uid;
+  int outsize = set_message(outbuf,2,3,True);
+  int max_count = SVAL(inbuf,smb_vwv0);
+  int start_index = SVAL(inbuf,smb_vwv1);
+
+  cnum = SVAL(inbuf,smb_tid);
+  uid = SVAL(inbuf,smb_uid);
+
+/* allow checking the queue for anyone */
+#if 0
+  if (!CAN_PRINT(cnum))
+    return(ERROR(ERRDOS,ERRnoaccess));
+#endif
+
+  SSVAL(outbuf,smb_vwv0,0);
+  SSVAL(outbuf,smb_vwv1,0);
+  CVAL(smb_buf(outbuf),0) = 1;
+  SSVAL(smb_buf(outbuf),1,0);
+  
+  DEBUG(3,("%s printqueue cnum=%d start_index=%d max_count=%d\n",
+       timestring(),cnum,start_index,max_count));
+
+  if (!OPEN_CNUM(cnum) || !Connections[cnum].printer)
+    {
+      int i;
+      cnum = -1;
+
+      for (i=0;i<MAX_CONNECTIONS;i++)
+       if (CAN_PRINT(i) && Connections[i].printer)
+         cnum = i;
+
+      if (cnum == -1)
+       for (i=0;i<MAX_CONNECTIONS;i++)
+         if (OPEN_CNUM(i))
+           cnum = i;
+
+      if (!OPEN_CNUM(cnum))
+       return(ERROR(ERRSRV,ERRinvnid));
+
+      DEBUG(5,("connection not open or not a printer, using cnum %d\n",cnum));
+    }
+
+  if (!become_user(cnum,uid))
+    return(ERROR(ERRSRV,ERRinvnid));
+
+  {
+    print_queue_struct *queue = NULL;
+    char *p = smb_buf(outbuf) + 3;
+    int count = get_printqueue(SNUM(cnum),cnum,&queue,NULL);
+    int num_to_get = ABS(max_count);
+    int first = (max_count>0?start_index:start_index+max_count+1);
+    int i;
+
+    if (first >= count)
+      num_to_get = 0;
+    else
+      num_to_get = MIN(num_to_get,count-first);
+    
+
+    for (i=first;i<first+num_to_get;i++)
+      {
+       put_dos_date2(p,0,queue[i].time);
+       CVAL(p,4) = (queue[i].status==LPQ_PRINTING?2:3);
+       SSVAL(p,5,queue[i].job);
+       SIVAL(p,7,queue[i].size);
+       CVAL(p,11) = 0;
+       StrnCpy(p+12,queue[i].user,16);
+       p += 28;
+      }
+
+    if (count > 0)
+      {
+       outsize = set_message(outbuf,2,28*count+3,False);         
+       SSVAL(outbuf,smb_vwv0,count);
+       SSVAL(outbuf,smb_vwv1,(max_count>0?first+count:first-1));
+       CVAL(smb_buf(outbuf),0) = 1;
+       SSVAL(smb_buf(outbuf),1,28*count);
+      }
+
+    if (queue) free(queue);
+         
+    DEBUG(3,("%d entries returned in queue\n",count));
+  }
+  
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a printwrite
+****************************************************************************/
+int reply_printwrite(char *inbuf,char *outbuf)
+{
+  int cnum,numtowrite,fnum;
+  int outsize = set_message(outbuf,0,0,True);
+  char *data;
+  
+  cnum = SVAL(inbuf,smb_tid);
+
+  if (!CAN_PRINT(cnum))
+    return(ERROR(ERRDOS,ERRnoaccess));
+
+  fnum = GETFNUM(inbuf,smb_vwv0);
+
+  CHECK_FNUM(fnum,cnum);
+  CHECK_WRITE(fnum);
+  CHECK_ERROR(fnum);
+
+  numtowrite = SVAL(smb_buf(inbuf),1);
+  data = smb_buf(inbuf) + 3;
+  
+  if (write_file(fnum,data,numtowrite) != numtowrite)
+    return(UNIXERROR(ERRDOS,ERRnoaccess));
+  
+  DEBUG(3,("%s printwrite fnum=%d cnum=%d num=%d\n",timestring(),fnum,cnum,numtowrite));
+  
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a mkdir
+****************************************************************************/
+int reply_mkdir(char *inbuf,char *outbuf)
+{
+  pstring directory;
+  int cnum;
+  int outsize,ret= -1;
+  
+  strcpy(directory,smb_buf(inbuf) + 1);
+  cnum = SVAL(inbuf,smb_tid);
+  unix_convert(directory,cnum);
+  
+  if (check_name(directory,cnum))
+    ret = sys_mkdir(directory,unix_mode(cnum,aDIR));
+  
+  if (ret < 0)
+    return(UNIXERROR(ERRDOS,ERRnoaccess));
+  
+  outsize = set_message(outbuf,0,0,True);
+  
+  DEBUG(3,("%s mkdir %s cnum=%d ret=%d\n",timestring(),directory,cnum,ret));
+  
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a rmdir
+****************************************************************************/
+int reply_rmdir(char *inbuf,char *outbuf)
+{
+  pstring directory;
+  int cnum;
+  int outsize = 0;
+  BOOL ok = False;
+  
+  cnum = SVAL(inbuf,smb_tid);
+  strcpy(directory,smb_buf(inbuf) + 1);
+  unix_convert(directory,cnum);
+  
+  if (check_name(directory,cnum))
+    {
+      dptr_closepath(directory,SVAL(inbuf,smb_pid));
+      ok = (sys_rmdir(directory) == 0);
+      if (!ok)
+       DEBUG(3,("couldn't remove directory %s : %s\n",
+                directory,strerror(errno)));
+    }
+  
+  if (!ok)
+    return(UNIXERROR(ERRDOS,ERRbadpath));
+  
+  outsize = set_message(outbuf,0,0,True);
+  
+  DEBUG(3,("%s rmdir %s\n",timestring(),directory));
+  
+  return(outsize);
+}
+
+
+/*******************************************************************
+resolve wildcards in a filename rename
+********************************************************************/
+static BOOL resolve_wildcards(char *name1,char *name2)
+{
+  fstring root1,root2;
+  fstring ext1,ext2;
+  char *p,*p2;
+
+  name1 = strrchr(name1,'/');
+  name2 = strrchr(name2,'/');
+
+  if (!name1 || !name2) return(False);
+  
+  strcpy(root1,name1);
+  strcpy(root2,name2);
+  p = strrchr(root1,'.');
+  if (p) {
+    *p = 0;
+    strcpy(ext1,p+1);
+  } else {
+    strcpy(ext1,"");    
+  }
+  p = strrchr(root2,'.');
+  if (p) {
+    *p = 0;
+    strcpy(ext2,p+1);
+  } else {
+    strcpy(ext2,"");    
+  }
+
+  p = root1;
+  p2 = root2;
+  while (*p2) {
+    if (*p2 == '?') {
+      *p2 = *p;
+      p2++;
+    } else {
+      p2++;
+    }
+    if (*p) p++;
+  }
+
+  p = ext1;
+  p2 = ext2;
+  while (*p2) {
+    if (*p2 == '?') {
+      *p2 = *p;
+      p2++;
+    } else {
+      p2++;
+    }
+    if (*p) p++;
+  }
+
+  strcpy(name2,root2);
+  if (ext2[0]) {
+    strcat(name2,".");
+    strcat(name2,ext2);
+  }
+
+  return(True);
+}
+
+/*******************************************************************
+check if a user is allowed to rename a file
+********************************************************************/
+static BOOL can_rename(char *fname,int cnum)
+{
+  struct stat sbuf;
+
+  if (!CAN_WRITE(cnum)) return(False);
+
+  if (sys_lstat(fname,&sbuf) != 0) return(False);
+  if (!check_file_sharing(cnum,fname)) return(False);
+
+  return(True);
+}
+
+/****************************************************************************
+  reply to a mv
+****************************************************************************/
+int reply_mv(char *inbuf,char *outbuf)
+{
+  int outsize = 0;
+  pstring name;
+  int cnum;
+  pstring directory;
+  pstring mask,newname;
+  char *p;
+  int count=0;
+  int error = ERRnoaccess;
+  BOOL has_wild;
+  BOOL exists=False;
+
+  *directory = *mask = 0;
+
+  cnum = SVAL(inbuf,smb_tid);
+  
+  strcpy(name,smb_buf(inbuf) + 1);
+  strcpy(newname,smb_buf(inbuf) + 3 + strlen(name));
+   
+  DEBUG(3,("reply_mv : %s -> %s\n",name,newname));
+   
+  unix_convert(name,cnum);
+  unix_convert(newname,cnum);
+
+  p = strrchr(name,'/');
+  if (!p) {
+    strcpy(directory,"./");
+    strcpy(mask,name);
+  } else {
+    *p = 0;
+    strcpy(directory,name);
+    strcpy(mask,p+1);
+  }
+
+  if (is_mangled(mask))
+    check_mangled_stack(mask);
+
+  has_wild = strchr(mask,'*') || strchr(mask,'?');
+
+  if (!has_wild) {
+    strcat(directory,"/");
+    strcat(directory,mask);
+    if (resolve_wildcards(directory,newname) && 
+       can_rename(directory,cnum) && 
+       !file_exist(newname,NULL) &&
+       !sys_rename(directory,newname)) count++;
+    if (!count) exists = file_exist(directory,NULL);
+    if (!count && exists && file_exist(newname,NULL)) {
+      exists = True;
+      error = 183;
+    }
+  } else {
+    void *dirptr = NULL;
+    char *dname;
+    pstring destname;
+
+    if (check_name(directory,cnum))
+      dirptr = OpenDir(directory);
+
+    if (dirptr)
+      {
+       error = ERRbadfile;
+
+       if (strequal(mask,"????????.???"))
+         strcpy(mask,"*");
+
+       while ((dname = ReadDirName(dirptr)))
+         {
+           pstring fname;
+           strcpy(fname,dname);
+           
+           if(!mask_match(fname, mask, case_sensitive, False)) continue;
+
+           error = ERRnoaccess;
+           sprintf(fname,"%s/%s",directory,dname);
+           if (!can_rename(fname,cnum)) continue;
+           strcpy(destname,newname);
+
+           if (!resolve_wildcards(fname,destname)) continue;
+
+           if (file_exist(destname,NULL)) {
+             error = 183;
+             continue;
+           }
+           if (!sys_rename(fname,destname)) count++;
+           DEBUG(3,("reply_mv : doing rename on %s -> %s\n",fname,destname));
+         }
+       CloseDir(dirptr);
+      }
+  }
+  
+  if (count == 0) {
+    if (exists)
+      return(ERROR(ERRDOS,error));
+    else
+      return(UNIXERROR(ERRDOS,error));
+  }
+  
+  outsize = set_message(outbuf,0,0,True);
+  
+  return(outsize);
+}
+
+/*******************************************************************
+  copy a file as part of a reply_copy
+  ******************************************************************/
+static BOOL copy_file(char *src,char *dest1,int cnum,int ofun,
+                     int count,BOOL target_is_directory)
+{
+  int Access,action;
+  struct stat st;
+  int ret=0;
+  int fnum1,fnum2;
+  pstring dest;
+  
+  strcpy(dest,dest1);
+  if (target_is_directory) {
+    char *p = strrchr(src,'/');
+    if (p) 
+      p++;
+    else
+      p = src;
+    strcat(dest,"/");
+    strcat(dest,p);
+  }
+
+  if (!file_exist(src,&st)) return(False);
+
+  fnum1 = find_free_file();
+  if (fnum1<0) return(False);
+  open_file_shared(fnum1,cnum,src,(DENY_NONE<<4),
+                  1,0,&Access,&action);
+
+  if (!Files[fnum1].open) return(False);
+
+  if (!target_is_directory && count)
+    ofun = 1;
+
+  fnum2 = find_free_file();
+  if (fnum2<0) {
+    close_file(fnum1);
+    return(False);
+  }
+  open_file_shared(fnum2,cnum,dest,(DENY_NONE<<4)|1,
+                  ofun,st.st_mode,&Access,&action);
+
+  if (!Files[fnum2].open) {
+    close_file(fnum1);
+    return(False);
+  }
+
+  if ((ofun&3) == 1) {
+    lseek(Files[fnum2].fd,0,SEEK_END);
+  }
+  
+  if (st.st_size)
+    ret = transfer_file(Files[fnum1].fd,Files[fnum2].fd,st.st_size,NULL,0,0);
+
+  close_file(fnum1);
+  close_file(fnum2);
+
+  return(ret == st.st_size);
+}
+
+
+
+/****************************************************************************
+  reply to a file copy.
+  ****************************************************************************/
+int reply_copy(char *inbuf,char *outbuf)
+{
+  int outsize = 0;
+  pstring name;
+  int cnum;
+  pstring directory;
+  pstring mask,newname;
+  char *p;
+  int count=0;
+  int error = ERRnoaccess;
+  BOOL has_wild;
+  BOOL exists=False;
+  int tid2 = SVAL(inbuf,smb_vwv0);
+  int ofun = SVAL(inbuf,smb_vwv1);
+  int flags = SVAL(inbuf,smb_vwv2);
+  BOOL target_is_directory=False;
+
+  *directory = *mask = 0;
+
+  cnum = SVAL(inbuf,smb_tid);
+  
+  strcpy(name,smb_buf(inbuf));
+  strcpy(newname,smb_buf(inbuf) + 1 + strlen(name));
+   
+  DEBUG(3,("reply_copy : %s -> %s\n",name,newname));
+   
+  if (tid2 != cnum) {
+    /* can't currently handle inter share copies XXXX */
+    DEBUG(3,("Rejecting inter-share copy\n"));
+    return(ERROR(ERRSRV,ERRinvdevice));
+  }
+
+  unix_convert(name,cnum);
+  unix_convert(newname,cnum);
+
+  target_is_directory = directory_exist(newname,NULL);
+
+  if ((flags&1) && target_is_directory) {
+    return(ERROR(ERRDOS,ERRbadfile));
+  }
+
+  if ((flags&2) && !target_is_directory) {
+    return(ERROR(ERRDOS,ERRbadpath));
+  }
+
+  if ((flags&(1<<5)) && directory_exist(name,NULL)) {
+    /* wants a tree copy! XXXX */
+    DEBUG(3,("Rejecting tree copy\n"));
+    return(ERROR(ERRSRV,ERRerror));    
+  }
+
+  p = strrchr(name,'/');
+  if (!p) {
+    strcpy(directory,"./");
+    strcpy(mask,name);
+  } else {
+    *p = 0;
+    strcpy(directory,name);
+    strcpy(mask,p+1);
+  }
+
+  if (is_mangled(mask))
+    check_mangled_stack(mask);
+
+  has_wild = strchr(mask,'*') || strchr(mask,'?');
+
+  if (!has_wild) {
+    strcat(directory,"/");
+    strcat(directory,mask);
+    if (resolve_wildcards(directory,newname) && 
+       copy_file(directory,newname,cnum,ofun,
+                 count,target_is_directory)) count++;
+    if (!count) exists = file_exist(directory,NULL);
+  } else {
+    void *dirptr = NULL;
+    char *dname;
+    pstring destname;
+
+    if (check_name(directory,cnum))
+      dirptr = OpenDir(directory);
+
+    if (dirptr)
+      {
+       error = ERRbadfile;
+
+       if (strequal(mask,"????????.???"))
+         strcpy(mask,"*");
+
+       while ((dname = ReadDirName(dirptr)))
+         {
+           pstring fname;
+           strcpy(fname,dname);
+           
+           if(!mask_match(fname, mask, case_sensitive, False)) continue;
+
+           error = ERRnoaccess;
+           sprintf(fname,"%s/%s",directory,dname);
+           strcpy(destname,newname);
+           if (resolve_wildcards(fname,destname) && 
+               copy_file(directory,newname,cnum,ofun,
+                         count,target_is_directory)) count++;
+           DEBUG(3,("reply_copy : doing copy on %s -> %s\n",fname,destname));
+         }
+       CloseDir(dirptr);
+      }
+  }
+  
+  if (count == 0) {
+    if (exists)
+      return(ERROR(ERRDOS,error));
+    else
+      return(UNIXERROR(ERRDOS,error));
+  }
+  
+  outsize = set_message(outbuf,1,0,True);
+  SSVAL(outbuf,smb_vwv0,count);
+
+  return(outsize);
+}
+
+
+
+/****************************************************************************
+  reply to a setdir
+****************************************************************************/
+int reply_setdir(char *inbuf,char *outbuf)
+{
+  int cnum,snum;
+  int outsize = 0;
+  BOOL ok = False;
+  pstring newdir;
+  
+  cnum = SVAL(inbuf,smb_tid);
+  
+  snum = Connections[cnum].service;
+  if (!CAN_SETDIR(snum))
+    return(ERROR(ERRDOS,ERRnoaccess));
+  
+  strcpy(newdir,smb_buf(inbuf) + 1);
+  strlower(newdir);
+  
+  if (strlen(newdir) == 0)
+    ok = True;
+  else
+    {
+      ok = directory_exist(newdir,NULL);
+      if (ok)
+       string_set(&Connections[cnum].connectpath,newdir);
+    }
+  
+  if (!ok)
+    return(ERROR(ERRDOS,ERRbadpath));
+  
+  outsize = set_message(outbuf,0,0,True);
+  CVAL(outbuf,smb_reh) = CVAL(inbuf,smb_reh);
+  
+  DEBUG(3,("%s setdir %s cnum=%d\n",timestring(),newdir,cnum));
+  
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a lockingX request
+****************************************************************************/
+int reply_lockingX(char *inbuf,char *outbuf,int length,int bufsize)
+{
+  int smb_com2 = CVAL(inbuf,smb_vwv0);
+  int smb_off2 = SVAL(inbuf,smb_vwv1);
+  int fnum = GETFNUM(inbuf,smb_vwv2);
+  uint16 locktype = SVAL(inbuf,smb_vwv3);
+  uint16 num_ulocks = SVAL(inbuf,smb_vwv6);
+  uint16 num_locks = SVAL(inbuf,smb_vwv7);
+  uint32 count, offset;
+
+  int cnum;
+  int i;
+  char *data;
+  uint32 ecode=0, dummy2;
+  int outsize, eclass=0, dummy1;
+
+  cnum = SVAL(inbuf,smb_tid);
+
+  CHECK_FNUM(fnum,cnum);
+  CHECK_ERROR(fnum);
+
+  data = smb_buf(inbuf);
+  /* Data now points at the beginning of the list
+     of smb_unlkrng structs */
+  for(i = 0; i < (int)num_ulocks; i++) {
+    count = IVAL(data,SMB_LKLEN_OFFSET(i));
+    offset = IVAL(data,SMB_LKOFF_OFFSET(i));
+    if(!do_unlock(fnum,cnum,count,offset,&eclass, &ecode))
+      return ERROR(eclass,ecode);
+  }
+
+  /* Now do any requested locks */
+  data += 10*num_ulocks;
+  /* Data now points at the beginning of the list
+     of smb_lkrng structs */
+  for(i = 0; i < (int)num_locks; i++) {
+    count = IVAL(data,SMB_LKLEN_OFFSET(i)); 
+    offset = IVAL(data,SMB_LKOFF_OFFSET(i)); 
+    if(!do_lock(fnum,cnum,count,offset, &eclass, &ecode))
+      break;
+  }
+
+  /* If any of the above locks failed, then we must unlock
+     all of the previous locks (X/Open spec). */
+  if(i != num_locks && num_locks != 0) {
+    for(; i >= 0; i--) {
+      count = IVAL(data,SMB_LKLEN_OFFSET(i));  
+      offset = IVAL(data,SMB_LKOFF_OFFSET(i)); 
+      do_unlock(fnum,cnum,count,offset,&dummy1,&dummy2);
+    }
+    return ERROR(eclass,ecode);
+  }
+
+  outsize = set_message(outbuf,2,0,True);
+  
+  CVAL(outbuf,smb_vwv0) = smb_com2;
+  SSVAL(outbuf,smb_vwv1,(outsize+chain_size)-4);
+  
+  DEBUG(3,("%s lockingX fnum=%d cnum=%d type=%d num_locks=%d num_ulocks=%d\n",
+       timestring(),fnum,cnum,locktype,num_locks,num_ulocks));
+
+  chain_fnum = fnum;
+
+  if (smb_com2 != 0xFF)
+    outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4,
+                          outbuf,outbuf+outsize,
+                          length,bufsize);
+  
+  chain_fnum = -1;
+  
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a SMBreadbmpx (read block multiplex) request
+****************************************************************************/
+int reply_readbmpx(char *inbuf,char *outbuf,int length,int bufsize)
+{
+  int cnum,fnum;
+  int nread = -1;
+  int total_read;
+  char *data;
+  int32 startpos;
+  int outsize, mincount, maxcount;
+  int max_per_packet;
+  int tcount;
+  int pad;
+
+  /* this function doesn't seem to work - disable by default */
+  if (!lp_readbmpx())
+    return(ERROR(ERRSRV,ERRuseSTD));
+
+  outsize = set_message(outbuf,8,0,True);
+
+  cnum = SVAL(inbuf,smb_tid);
+  fnum = GETFNUM(inbuf,smb_vwv0);
+
+  CHECK_FNUM(fnum,cnum);
+  CHECK_READ(fnum);
+  CHECK_ERROR(fnum);
+
+  startpos = IVAL(inbuf,smb_vwv1);
+  maxcount = SVAL(inbuf,smb_vwv3);
+  mincount = SVAL(inbuf,smb_vwv4);
+
+  data = smb_buf(outbuf);
+  pad = ((int)data)%4;
+  if (pad) pad = 4 - pad;
+  data += pad;
+
+  max_per_packet = bufsize-(outsize+pad);
+  tcount = maxcount;
+  total_read = 0;
+
+  if (is_locked(fnum,cnum,maxcount,startpos))
+    return(ERROR(ERRDOS,ERRlock));
+       
+  do
+    {
+      int N = MIN(max_per_packet,tcount-total_read);
+  
+      nread = read_file(fnum,data,startpos,N,N,-1,False);
+
+      if (nread <= 0) nread = 0;
+
+      if (nread < N)
+       tcount = total_read + nread;
+
+      set_message(outbuf,8,nread,False);
+      SIVAL(outbuf,smb_vwv0,startpos);
+      SSVAL(outbuf,smb_vwv2,tcount);
+      SSVAL(outbuf,smb_vwv6,nread);
+      SSVAL(outbuf,smb_vwv7,smb_offset(data,outbuf));
+
+      send_smb(Client,outbuf);
+
+      total_read += nread;
+      startpos += nread;
+    }
+  while (total_read < tcount);
+
+  return(-1);
+}
+
+
+/****************************************************************************
+  reply to a SMBwritebmpx (write block multiplex primary) request
+****************************************************************************/
+int reply_writebmpx(char *inbuf,char *outbuf)
+{
+  int cnum,numtowrite,fnum;
+  int nwritten = -1;
+  int outsize = 0;
+  int32 startpos;
+  int tcount, write_through, smb_doff;
+  char *data;
+  
+  cnum = SVAL(inbuf,smb_tid);
+  fnum = GETFNUM(inbuf,smb_vwv0);
+
+  CHECK_FNUM(fnum,cnum);
+  CHECK_WRITE(fnum);
+  CHECK_ERROR(fnum);
+
+  tcount = SVAL(inbuf,smb_vwv1);
+  startpos = IVAL(inbuf,smb_vwv3);
+  write_through = BITSETW(inbuf+smb_vwv7,0);
+  numtowrite = SVAL(inbuf,smb_vwv10);
+  smb_doff = SVAL(inbuf,smb_vwv11);
+
+  data = smb_base(inbuf) + smb_doff;
+
+  /* If this fails we need to send an SMBwriteC response,
+     not an SMBwritebmpx - set this up now so we don't forget */
+  CVAL(outbuf,smb_com) = SMBwritec;
+
+  if (is_locked(fnum,cnum,tcount,startpos))
+    return(ERROR(ERRDOS,ERRlock));
+
+  seek_file(fnum,startpos);
+  nwritten = write_file(fnum,data,numtowrite);
+
+  if(lp_syncalways(SNUM(cnum)) || write_through)
+    sync_file(fnum);
+  
+  if(nwritten < numtowrite)
+    return(UNIXERROR(ERRHRD,ERRdiskfull));
+
+  /* If the maximum to be written to this file
+     is greater than what we just wrote then set
+     up a secondary struct to be attached to this
+     fd, we will use this to cache error messages etc. */
+  if(tcount > nwritten) 
+    {
+      write_bmpx_struct *wbms;
+      if(Files[fnum].wbmpx_ptr != NULL)
+       wbms = Files[fnum].wbmpx_ptr; /* Use an existing struct */
+      else
+       wbms = (write_bmpx_struct *)malloc(sizeof(write_bmpx_struct));
+      if(!wbms)
+       {
+         DEBUG(0,("Out of memory in reply_readmpx\n"));
+         return(ERROR(ERRSRV,ERRnoresource));
+       }
+      wbms->wr_mode = write_through;
+      wbms->wr_discard = False; /* No errors yet */
+      wbms->wr_total_written = nwritten;
+      wbms->wr_errclass = 0;
+      wbms->wr_error = 0;
+      Files[fnum].wbmpx_ptr = wbms;
+    }
+
+  /* We are returning successfully, set the message type back to
+     SMBwritebmpx */
+  CVAL(outbuf,smb_com) = SMBwriteBmpx;
+  
+  outsize = set_message(outbuf,1,0,True);
+  
+  SSVALS(outbuf,smb_vwv0,-1); /* We don't support smb_remaining */
+  
+  DEBUG(3,("%s writebmpx fnum=%d cnum=%d num=%d wrote=%d\n",
+       timestring(),fnum,cnum,numtowrite,nwritten));
+  
+  if (write_through && tcount==nwritten) {
+    /* we need to send both a primary and a secondary response */
+    smb_setlen(outbuf,outsize - 4);
+    send_smb(Client,outbuf);
+
+    /* now the secondary */
+    outsize = set_message(outbuf,1,0,True);
+    CVAL(outbuf,smb_com) = SMBwritec;
+    SSVAL(outbuf,smb_vwv0,nwritten);
+  }
+
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a SMBwritebs (write block multiplex secondary) request
+****************************************************************************/
+int reply_writebs(char *inbuf,char *outbuf)
+{
+  int cnum,numtowrite,fnum;
+  int nwritten = -1;
+  int outsize = 0;
+  int32 startpos;
+  int tcount, write_through, smb_doff;
+  char *data;
+  write_bmpx_struct *wbms;
+  BOOL send_response = False;
+  
+  cnum = SVAL(inbuf,smb_tid);
+  fnum = GETFNUM(inbuf,smb_vwv0);
+  CHECK_FNUM(fnum,cnum);
+  CHECK_WRITE(fnum);
+
+  tcount = SVAL(inbuf,smb_vwv1);
+  startpos = IVAL(inbuf,smb_vwv2);
+  numtowrite = SVAL(inbuf,smb_vwv6);
+  smb_doff = SVAL(inbuf,smb_vwv7);
+
+  data = smb_base(inbuf) + smb_doff;
+
+  /* We need to send an SMBwriteC response, not an SMBwritebs */
+  CVAL(outbuf,smb_com) = SMBwritec;
+
+  /* This fd should have an auxiliary struct attached,
+     check that it does */
+  wbms = Files[fnum].wbmpx_ptr;
+  if(!wbms) return(-1);
+
+  /* If write through is set we can return errors, else we must
+     cache them */
+  write_through = wbms->wr_mode;
+
+  /* Check for an earlier error */
+  if(wbms->wr_discard)
+    return -1; /* Just discard the packet */
+
+  seek_file(fnum,startpos);
+  nwritten = write_file(fnum,data,numtowrite);
+
+  if(lp_syncalways(SNUM(cnum)) || write_through)
+    sync_file(fnum);
+  
+  if (nwritten < numtowrite)
+    {
+      if(write_through)        {
+       /* We are returning an error - we can delete the aux struct */
+       if (wbms) free((char *)wbms);
+       Files[fnum].wbmpx_ptr = NULL;
+       return(ERROR(ERRHRD,ERRdiskfull));
+      }
+      return(CACHE_ERROR(wbms,ERRHRD,ERRdiskfull));
+    }
+
+  /* Increment the total written, if this matches tcount
+     we can discard the auxiliary struct (hurrah !) and return a writeC */
+  wbms->wr_total_written += nwritten;
+  if(wbms->wr_total_written >= tcount)
+    {
+      if (write_through) {
+       outsize = set_message(outbuf,1,0,True);
+       SSVAL(outbuf,smb_vwv0,wbms->wr_total_written);    
+       send_response = True;
+      }
+
+      free((char *)wbms);
+      Files[fnum].wbmpx_ptr = NULL;
+    }
+
+  if(send_response)
+    return(outsize);
+
+  return(-1);
+}
+
+
+/****************************************************************************
+  reply to a SMBsetattrE
+****************************************************************************/
+int reply_setattrE(char *inbuf,char *outbuf)
+{
+  int cnum,fnum;
+  struct utimbuf unix_times;
+  int outsize = 0;
+
+  outsize = set_message(outbuf,0,0,True);
+
+  cnum = SVAL(inbuf,smb_tid);
+  fnum = GETFNUM(inbuf,smb_vwv0);
+
+  CHECK_FNUM(fnum,cnum);
+  CHECK_ERROR(fnum);
+
+  /* Convert the DOS times into unix times. Ignore create
+     time as UNIX can't set this.
+     */
+  unix_times.actime = make_unix_date2(inbuf+smb_vwv3);
+  unix_times.modtime = make_unix_date2(inbuf+smb_vwv5);
+  
+  /* Set the date on this file */
+  if(sys_utime(Files[fnum].name, &unix_times))
+    return(ERROR(ERRDOS,ERRnoaccess));
+  
+  DEBUG(3,("%s reply_setattrE fnum=%d cnum=%d\n",timestring(),fnum,cnum));
+
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a SMBgetattrE
+****************************************************************************/
+int reply_getattrE(char *inbuf,char *outbuf)
+{
+  int cnum,fnum;
+  struct stat sbuf;
+  int outsize = 0;
+  int mode;
+
+  outsize = set_message(outbuf,11,0,True);
+
+  cnum = SVAL(inbuf,smb_tid);
+  fnum = GETFNUM(inbuf,smb_vwv0);
+
+  CHECK_FNUM(fnum,cnum);
+  CHECK_ERROR(fnum);
+
+  /* Do an fstat on this file */
+  if(fstat(Files[fnum].fd, &sbuf))
+    return(UNIXERROR(ERRDOS,ERRnoaccess));
+  
+  mode = dos_mode(cnum,Files[fnum].name,&sbuf);
+  
+  /* Convert the times into dos times. Set create
+     date to be last modify date as UNIX doesn't save
+     this */
+  put_dos_date2(outbuf,smb_vwv0,sbuf.st_mtime);
+  put_dos_date2(outbuf,smb_vwv2,sbuf.st_atime);
+  put_dos_date2(outbuf,smb_vwv4,sbuf.st_mtime);
+  if (mode & aDIR)
+    {
+      SIVAL(outbuf,smb_vwv6,0);
+      SIVAL(outbuf,smb_vwv8,0);
+    }
+  else
+    {
+      SIVAL(outbuf,smb_vwv6,sbuf.st_size);
+      SIVAL(outbuf,smb_vwv8,ROUNDUP(sbuf.st_size,1024));
+    }
+  SSVAL(outbuf,smb_vwv10, mode);
+  
+  DEBUG(3,("%s reply_getattrE fnum=%d cnum=%d\n",timestring(),fnum,cnum));
+  
+  return(outsize);
+}
+
+
+
+
+
diff --git a/source/smbd/server.c b/source/smbd/server.c
new file mode 100644 (file)
index 0000000..5d8face
--- /dev/null
@@ -0,0 +1,4300 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   Main SMB server routines
+   Copyright (C) Andrew Tridgell 1992-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"
+#include "loadparm.h"
+#include "pcap.h"
+#include "trans2.h"
+#include "reply.h"
+
+pstring servicesf = CONFIGFILE;
+pstring OriginalDir ="/";
+extern pstring debugf;
+extern pstring sesssetup_user;
+
+char *InBuffer = NULL;
+char *OutBuffer = NULL;
+char *last_inbuf = NULL;
+
+int initial_uid = 0;
+int initial_gid = 0;
+
+BOOL share_mode_pending = False;
+
+/* have I done a become_user? */
+static struct {
+  int cnum, uid;
+} last_user;
+
+/* the last message the was processed */
+int last_message = -1;
+
+/* a useful macro to debug the last message processed */
+#define LAST_MESSAGE() smb_fn_name(last_message)
+
+extern pstring scope;
+extern int DEBUGLEVEL;
+extern int case_default;
+extern BOOL case_sensitive;
+extern BOOL case_preserve;
+extern BOOL use_mangled_map;
+extern BOOL short_case_preserve;
+extern BOOL case_mangle;
+extern time_t smb_last_time;
+
+extern pstring user_socket_options;
+
+connection_struct Connections[MAX_CONNECTIONS];
+files_struct Files[MAX_OPEN_FILES];
+
+extern int Protocol;
+
+int maxxmit = BUFFER_SIZE;
+
+int chain_size = 0;
+
+/* a fnum to use when chaining */
+int chain_fnum = -1;
+
+/* number of open connections */
+static int num_connections_open = 0;
+
+extern fstring remote_machine;
+
+
+/* these can be set by some functions to override the error codes */
+int unix_ERR_class=SUCCESS;
+int unix_ERR_code=0;
+
+
+extern int extra_time_offset;
+
+extern pstring myhostname;
+extern struct in_addr myip;
+
+
+static int find_free_connection(int hash);
+
+#ifdef SMB_PASSWD
+extern void generate_next_challenge(char *challenge);
+extern void set_challenge(char *challenge);
+#endif
+
+/* 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)
+
+
+
+/****************************************************************************
+  change a dos mode to a unix mode
+    base permission for files:
+         everybody gets read bit set
+         dos readonly is represented in unix by removing everyone's write bit
+         dos archive is represented in unix by the user's execute bit
+         dos system is represented in unix by the group's execute bit
+         dos hidden is represented in unix by the other's execute bit
+    base permission for directories:
+         dos directory is represented in unix by unix's dir bit and the exec bit
+****************************************************************************/
+mode_t unix_mode(int cnum,int dosmode)
+{
+  mode_t result = (S_IRUSR | S_IRGRP | S_IROTH);
+
+  if ( !IS_DOS_READONLY(dosmode) )
+    result |= (S_IWUSR | S_IWGRP | S_IWOTH);
+  if (IS_DOS_DIR(dosmode))
+    result |= (S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH | S_IWUSR);
+  if (MAP_ARCHIVE(cnum) && IS_DOS_ARCHIVE(dosmode))
+    result |= S_IXUSR;
+
+  if (MAP_SYSTEM(cnum) && IS_DOS_SYSTEM(dosmode))
+    result |= S_IXGRP;
+  if (MAP_HIDDEN(cnum) && IS_DOS_HIDDEN(dosmode))
+    result |= S_IXOTH;  
+  result &= CREATE_MODE(cnum);
+  return(result);
+}
+
+
+/****************************************************************************
+  change a unix mode to a dos mode
+****************************************************************************/
+int dos_mode(int cnum,char *path,struct stat *sbuf)
+{
+  int result = 0;
+
+#if OLD_DOS_MODE
+  if (!CAN_WRITE(cnum) || !((sbuf->st_mode & S_IWOTH) ||
+                           Connections[cnum].admin_user ||
+                           ((sbuf->st_mode & S_IWUSR) && 
+                            Connections[cnum].uid==sbuf->st_uid) ||
+                           ((sbuf->st_mode & S_IWGRP) && 
+                            in_group(sbuf->st_gid,Connections[cnum].gid,
+                                     Connections[cnum].ngroups,
+                                     Connections[cnum].igroups))))
+    result |= aRONLY;
+#else
+  if (CAN_WRITE(cnum) && !lp_alternate_permissions(SNUM(cnum))) {
+    if (!((sbuf->st_mode & S_IWOTH) ||
+         Connections[cnum].admin_user ||
+         ((sbuf->st_mode & S_IWUSR) && Connections[cnum].uid==sbuf->st_uid) ||
+         ((sbuf->st_mode & S_IWGRP) && 
+          in_group(sbuf->st_gid,Connections[cnum].gid,
+                   Connections[cnum].ngroups,Connections[cnum].igroups))))
+      result |= aRONLY;
+  } else {
+    if ((sbuf->st_mode & S_IWUSR) == 0)
+      result |= aRONLY;
+  }
+#endif
+
+  if ((sbuf->st_mode & S_IXUSR) != 0)
+    result |= aARCH;
+
+  if (MAP_SYSTEM(cnum) && ((sbuf->st_mode & S_IXGRP) != 0))
+    result |= aSYSTEM;
+
+  if (MAP_HIDDEN(cnum) && ((sbuf->st_mode & S_IXOTH) != 0))
+    result |= aHIDDEN;   
+  
+  if (S_ISDIR(sbuf->st_mode))
+    result = aDIR | (result & aRONLY);
+
+#if LINKS_READ_ONLY
+  if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
+    result |= aRONLY;
+#endif
+
+  /* hide files with a name starting with a . */
+  if (lp_hide_dot_files(SNUM(cnum)))
+    {
+      char *p = strrchr(path,'/');
+      if (p)
+       p++;
+      else
+       p = path;
+      
+      if (p[0] == '.' && p[1] != '.' && p[1] != 0)
+       result |= aHIDDEN;
+    }
+
+  return(result);
+}
+
+
+/*******************************************************************
+chmod a file - but preserve some bits
+********************************************************************/
+int dos_chmod(int cnum,char *fname,int dosmode,struct stat *st)
+{
+  struct stat st1;
+  int mask=0;
+  int tmp;
+  int unixmode;
+
+  if (!st) {
+    st = &st1;
+    if (sys_stat(fname,st)) return(-1);
+  }
+
+  if (S_ISDIR(st->st_mode)) dosmode |= aDIR;
+
+  if (dos_mode(cnum,fname,st) == dosmode) return(0);
+
+  unixmode = unix_mode(cnum,dosmode);
+
+  /* preserve the s bits */
+  mask |= (S_ISUID | S_ISGID);
+
+  /* preserve the t bit */
+#ifdef S_ISVTX
+  mask |= S_ISVTX;
+#endif
+
+  /* possibly preserve the x bits */
+  if (!MAP_ARCHIVE(cnum)) mask |= S_IXUSR;
+  if (!MAP_SYSTEM(cnum)) mask |= S_IXGRP;
+  if (!MAP_HIDDEN(cnum)) mask |= S_IXOTH;
+
+  unixmode |= (st->st_mode & mask);
+
+  /* if we previously had any r bits set then leave them alone */
+  if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
+    unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
+    unixmode |= tmp;
+  }
+
+  /* if we previously had any w bits set then leave them alone 
+   if the new mode is not rdonly */
+  if (!IS_DOS_READONLY(dosmode) &&
+      (tmp = st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH))) {
+    unixmode &= ~(S_IWUSR|S_IWGRP|S_IWOTH);
+    unixmode |= tmp;
+  }
+    
+  return(chmod(fname,unixmode));
+}
+
+
+/****************************************************************************
+check if two filenames are equal
+
+this needs to be careful about whether we are case sensitive
+****************************************************************************/
+static BOOL fname_equal(char *name1, char *name2)
+{
+  int l1 = strlen(name1);
+  int l2 = strlen(name2);
+
+  /* handle filenames ending in a single dot */
+  if (l1-l2 == 1 && name1[l1-1] == '.' && lp_strip_dot())
+    {
+      BOOL ret;
+      name1[l1-1] = 0;
+      ret = fname_equal(name1,name2);
+      name1[l1-1] = '.';
+      return(ret);
+    }
+
+  if (l2-l1 == 1 && name2[l2-1] == '.' && lp_strip_dot())
+    {
+      BOOL ret;
+      name2[l2-1] = 0;
+      ret = fname_equal(name1,name2);
+      name2[l2-1] = '.';
+      return(ret);
+    }
+
+  /* now normal filename handling */
+  if (case_sensitive)
+    return(strcmp(name1,name2) == 0);
+
+  return(strequal(name1,name2));
+}
+
+
+/****************************************************************************
+mangle the 2nd name and check if it is then equal to the first name
+****************************************************************************/
+static BOOL mangled_equal(char *name1, char *name2)
+{
+  pstring tmpname;
+
+  if (is_8_3(name2))
+    return(False);
+
+  strcpy(tmpname,name2);
+  mangle_name_83(tmpname);
+
+  return(strequal(name1,tmpname));
+}
+
+
+/****************************************************************************
+scan a directory to find a filename, matching without case sensitivity
+
+If the name looks like a mangled name then try via the mangling functions
+****************************************************************************/
+static BOOL scan_directory(char *path, char *name,int snum,BOOL docache)
+{
+  void *cur_dir;
+  char *dname;
+  BOOL mangled;
+  fstring name2;
+
+  mangled = is_mangled(name);
+
+  /* handle null paths */
+  if (*path == 0)
+    path = ".";
+
+  if (docache && (dname = DirCacheCheck(path,name,snum))) {
+    strcpy(name, dname);       
+    return(True);
+  }      
+
+  if (mangled)
+    check_mangled_stack(name);
+
+  /* open the directory */
+  if (!(cur_dir = OpenDir(path))) 
+    {
+      DEBUG(3,("scan dir didn't open dir [%s]\n",path));
+      return(False);
+    }
+
+  /* now scan for matching names */
+  while ((dname = ReadDirName(cur_dir))) 
+    {
+      if (*dname == '.' &&
+         (strequal(dname,".") || strequal(dname,"..")))
+       continue;
+
+      strcpy(name2,dname);
+      if (!name_map_mangle(name2,False,snum)) continue;
+
+      if ((mangled && mangled_equal(name,name2))
+         || fname_equal(name, name2))
+       {
+         /* we've found the file, change it's name and return */
+         if (docache) DirCacheAdd(path,name,dname,snum);
+         strcpy(name, dname);
+         CloseDir(cur_dir);
+         return(True);
+       }
+    }
+
+  CloseDir(cur_dir);
+  return(False);
+}
+
+/****************************************************************************
+This routine is called to convert names from the dos namespace to unix
+namespace. It needs to handle any case conversions, mangling, format
+changes etc.
+
+We assume that we have already done a chdir() to the right "root" directory
+for this service.
+
+The function will return False if some part of the name except for the last
+part cannot be resolved
+****************************************************************************/
+BOOL unix_convert(char *name,int cnum)
+{
+  struct stat st;
+  char *start, *end;
+  pstring dirpath;
+
+  *dirpath = 0;
+
+  /* convert to basic unix format - removing \ chars and cleaning it up */
+  unix_format(name);
+  unix_clean_name(name);
+
+  if (!case_sensitive && 
+      (!case_preserve || (is_8_3(name) && !short_case_preserve)))
+    strnorm(name);
+
+  /* names must be relative to the root of the service - trim any leading /.
+   also trim trailing /'s */
+  trim_string(name,"/","/");
+
+  /* check if it's a printer file */
+  if (Connections[cnum].printer)
+    {
+      if ((! *name) || strchr(name,'/') || !is_8_3(name))
+       {
+         fstring name2;
+         sprintf(name2,"%.6s.XXXXXX",remote_machine);
+         strcpy(name,(char *)mktemp(name2));     
+       }      
+      return(True);
+    }
+
+  /* stat the name - if it exists then we are all done! */
+  if (sys_stat(name,&st) == 0)
+    return(True);
+
+  DEBUG(5,("unix_convert(%s,%d)\n",name,cnum));
+
+  /* a special case - if we don't have any mangling chars and are case
+     sensitive then searching won't help */
+  if (case_sensitive && !is_mangled(name) && 
+      !lp_strip_dot() && !use_mangled_map)
+    return(False);
+
+  /* now we need to recursively match the name against the real 
+     directory structure */
+
+  start = name;
+  while (strncmp(start,"./",2) == 0)
+    start += 2;
+
+  /* now match each part of the path name separately, trying the names
+     as is first, then trying to scan the directory for matching names */
+  for (;start;start = (end?end+1:(char *)NULL)) 
+    {
+      /* pinpoint the end of this section of the filename */
+      end = strchr(start, '/');
+
+      /* chop the name at this point */
+      if (end) *end = 0;
+
+      /* check if the name exists up to this point */
+      if (sys_stat(name, &st) == 0) 
+       {
+         /* it exists. it must either be a directory or this must be
+            the last part of the path for it to be OK */
+         if (end && !(st.st_mode & S_IFDIR)) 
+           {
+             /* an intermediate part of the name isn't a directory */
+             DEBUG(5,("Not a dir %s\n",start));
+             *end = '/';
+             return(False);
+           }
+       }
+      else 
+       {
+         pstring rest;
+
+         *rest = 0;
+
+         /* remember the rest of the pathname so it can be restored
+            later */
+         if (end) strcpy(rest,end+1);
+
+
+         /* try to find this part of the path in the directory */
+         if (strchr(start,'?') || strchr(start,'*') ||
+             !scan_directory(dirpath, start, SNUM(cnum), end?True:False))
+           {
+             if (end) 
+               {
+                 /* an intermediate part of the name can't be found */
+                 DEBUG(5,("Intermediate not found %s\n",start));
+                 *end = '/';
+                 return(False);
+               }
+             
+             /* just the last part of the name doesn't exist */
+             /* we may need to strupper() or strlower() it in case
+                this conversion is being used for file creation 
+                purposes */
+             /* if the filename is of mixed case then don't normalise it */
+             if (!case_preserve && 
+                 (!strhasupper(start) || !strhaslower(start)))         
+               strnorm(start);
+
+             /* check on the mangled stack to see if we can recover the 
+                base of the filename */
+             if (is_mangled(start))
+               check_mangled_stack(start);
+
+             DEBUG(5,("New file %s\n",start));
+             return(True); 
+           }
+
+         /* restore the rest of the string */
+         if (end) 
+           {
+             strcpy(start+strlen(start)+1,rest);
+             end = start + strlen(start);
+           }
+       }
+
+      /* add to the dirpath that we have resolved so far */
+      if (*dirpath) strcat(dirpath,"/");
+      strcat(dirpath,start);
+
+      /* restore the / that we wiped out earlier */
+      if (end) *end = '/';
+    }
+  
+  /* the name has been resolved */
+  DEBUG(5,("conversion finished %s\n",name));
+  return(True);
+}
+
+
+
+
+#ifdef QUOTAS
+#ifdef LINUX
+/****************************************************************************
+try to get the disk space from disk quotas (LINUX version)
+****************************************************************************/
+/*
+If you didn't make the symlink to the quota package, too bad :(
+*/
+#include "quota/quotactl.c"
+#include "quota/hasquota.c"
+static BOOL disk_quotas(char *path, int *bsize, int *dfree, int *dsize)
+{
+  uid_t euser_id;
+  struct dqblk D;
+  struct stat S;
+  dev_t devno ;
+  struct mntent *mnt;
+  FILE *fp;
+  int found ;
+  int qcmd, fd ;
+  char *qfpathname;
+  
+  /* find the block device file */
+  
+  if ( stat(path, &S) == -1 )
+    return(False) ;
+
+  devno = S.st_dev ;
+  
+  fp = setmntent(MOUNTED,"r");
+  found = False ;
+  
+  while ((mnt = getmntent(fp)) != (struct mntent *) 0) {
+    if ( stat(mnt->mnt_dir,&S) == -1 )
+      continue ;
+    if (S.st_dev == devno) {
+      found = True ;
+      break ;
+    }
+  }
+  endmntent(fp) ;
+  
+  if ( ! found )
+    return(False) ;
+  
+  qcmd = QCMD(Q_GETQUOTA, USRQUOTA);
+  
+  if (hasmntopt(mnt, MNTOPT_NOAUTO) || hasmntopt(mnt, MNTOPT_NOQUOTA))
+    return(False) ;
+  
+  if (!hasquota(mnt, USRQUOTA, &qfpathname))
+    return(False) ;
+  
+  euser_id = geteuid();
+  seteuid(0);
+  
+  if (quotactl(qcmd, mnt->mnt_fsname, euser_id, (caddr_t)&D) != 0) {
+    if ((fd = open(qfpathname, O_RDONLY)) < 0) {
+      seteuid(euser_id);
+      return(False);
+    }
+    lseek(fd, (long) dqoff(euser_id), L_SET);
+    switch (read(fd, &D, sizeof(struct dqblk))) {
+    case 0:/* EOF */
+      memset((caddr_t)&D, 0, sizeof(struct dqblk));
+      break;
+    case sizeof(struct dqblk):   /* OK */
+      break;
+    default:   /* ERROR */
+      close(fd);
+      seteuid(euser_id);
+      return(False);
+    }
+  }
+  seteuid(euser_id);
+  *bsize=1024;
+
+  if (D.dqb_bsoftlimit==0)
+    return(False);
+  if ((D.dqb_curblocks>D.dqb_bsoftlimit)||(D.dqb_curinodes>D.dqb_isoftlimit))
+    {
+      *dfree = 0;
+      *dsize = D.dqb_curblocks;
+    }
+  else {
+    *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
+    *dsize = D.dqb_bsoftlimit;
+  }
+  return (True);
+}
+#else
+#ifndef CRAY
+/****************************************************************************
+try to get the disk space from disk quotas
+****************************************************************************/
+static BOOL disk_quotas(char *path, int *bsize, int *dfree, int *dsize)
+{
+  uid_t user_id, euser_id;
+  int r;
+  char dev_disk[256];
+  struct dqblk D;
+  struct stat S;
+  /* find the block device file */
+  if ((stat(path, &S)<0) ||
+      (devnm(S_IFBLK, S.st_dev, dev_disk, 256, 0)<0)) return (False);
+
+  euser_id = geteuid();
+
+#ifdef USE_SETRES
+  /* for HPUX, real uid must be same as euid to execute quotactl for euid */
+  user_id = getuid();
+  setresuid(euser_id,-1,-1);
+#endif
+  r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D);
+  #ifdef USE_SETRES
+  if (setresuid(user_id,-1,-1))
+    DEBUG(5,("Unable to reset uid to %d\n", user_id));
+  #endif
+  /* Use softlimit to determine disk space, except when it has been exceeded */
+  *bsize = 1024;
+  if (r)
+    {
+      if (errno == EDQUOT) 
+       {
+         *dfree =0;
+         *dsize =D.dqb_curblocks;
+         return (True);
+       }
+      else return(False);
+    }
+  /* Use softlimit to determine disk space, except when it has been exceeded */
+  if ((D.dqb_curblocks>D.dqb_bsoftlimit)||(D.dqb_curfiles>D.dqb_fsoftlimit)) 
+    {
+      *dfree = 0;
+      *dsize = D.dqb_curblocks;
+    }
+  else {
+    *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
+    *dsize = D.dqb_bsoftlimit;
+  }
+  return (True);
+}
+#else
+/****************************************************************************
+try to get the disk space from disk quotas (CRAY VERSION)
+****************************************************************************/
+static BOOL disk_quotas(char *path, int *bsize, int *dfree, int *dsize)
+{
+  struct mntent *mnt;
+  FILE *fd;
+  struct stat sbuf;
+  dev_t devno ;
+  static dev_t devno_cached = 0 ;
+  static char name[MNTMAXSTR] ;
+  struct q_request request ;
+  struct qf_header header ;
+  static int quota_default = 0 ;
+  int found ;
+  
+  if ( stat(path,&sbuf) == -1 )
+    return(False) ;
+  
+  devno = sbuf.st_dev ;
+  
+  if ( devno != devno_cached ) {
+    
+    devno_cached = devno ;
+    
+    if ((fd = setmntent(KMTAB)) == NULL)
+      return(False) ;
+    
+    found = False ;
+    
+    while ((mnt = getmntent(fd)) != NULL) {
+      
+      if ( stat(mnt->mnt_dir,&sbuf) == -1 )
+       continue ;
+      
+      if (sbuf.st_dev == devno) {
+       
+       found = True ;
+       break ;
+       
+      }
+      
+    }
+    
+    strcpy(name,mnt->mnt_dir) ;
+    endmntent(fd) ;
+    
+    if ( ! found )
+      return(False) ;
+  }
+  
+  request.qf_magic = QF_MAGIC ;
+  request.qf_entry.id = geteuid() ;
+  
+  if (quotactl(name, Q_GETQUOTA, &request) == -1)
+    return(False) ;
+  
+  if ( ! request.user )
+    return(False) ;
+  
+  if ( request.qf_entry.user_q.f_quota == QFV_DEFAULT ) {
+    
+    if ( ! quota_default ) {
+      
+      if ( quotactl(name, Q_GETHEADER, &header) == -1 )
+       return(False) ;
+      else
+       quota_default = header.user_h.def_fq ;
+    }
+    
+    *dfree = quota_default ;
+    
+  }else if ( request.qf_entry.user_q.f_quota == QFV_PREVENT ) {
+    
+    *dfree = 0 ;
+    
+  }else{
+    
+    *dfree = request.qf_entry.user_q.f_quota ;
+    
+  }
+  
+  *dsize = request.qf_entry.user_q.f_use ;
+  
+  if ( *dfree )
+    *dfree -= *dsize ;
+  
+  if ( *dfree < 0 )
+    *dfree = 0 ;
+  
+  *bsize = 4096 ;  /* Cray blocksize */
+  
+  return(True) ;
+  
+}
+#endif /* CRAY */
+#endif /* LINUX */
+#endif /* QUOTAS */
+
+
+/****************************************************************************
+normalise for DOS usage 
+****************************************************************************/
+static void disk_norm(int *bsize,int *dfree,int *dsize)
+{
+  /* check if the disk is beyond the max disk size */
+  int maxdisksize = lp_maxdisksize();
+  if (maxdisksize) {
+    /* convert to blocks - and don't overflow */
+    maxdisksize = ((maxdisksize*1024)/(*bsize))*1024;
+    if (*dsize > maxdisksize) *dsize = maxdisksize;
+    if (*dfree > maxdisksize) *dfree = maxdisksize-1; /* the -1 should stop 
+                                                        applications getting 
+                                                        div by 0 errors */
+  }  
+
+  while (*dfree > WORDMAX || *dsize > WORDMAX || *bsize < 512) 
+    {
+      *dfree /= 2;
+      *dsize /= 2;
+      *bsize *= 2;
+      if (*bsize > WORDMAX )
+       {
+         *bsize = WORDMAX;
+         if (*dsize > WORDMAX)
+           *dsize = WORDMAX;
+         if (*dfree >  WORDMAX)
+           *dfree = WORDMAX;
+         break;
+       }
+    }
+}
+
+/****************************************************************************
+  return number of 1K blocks available on a path and total number 
+****************************************************************************/
+int disk_free(char *path,int *bsize,int *dfree,int *dsize)
+{
+  char *df_command = lp_dfree_command();
+#ifndef NO_STATFS
+#ifdef USE_STATVFS
+  struct statvfs fs;
+#else
+#ifdef ULTRIX
+  struct fs_data fs;
+#else
+  struct statfs fs;
+#endif
+#endif
+#endif
+
+#ifdef QUOTAS
+  if (disk_quotas(path, bsize, dfree, dsize))
+    {
+      disk_norm(bsize,dfree,dsize);
+      return(((*bsize)/1024)*(*dfree));
+    }
+#endif
+
+
+  /* possibly use system() to get the result */
+  if (df_command && *df_command)
+    {
+      int ret;
+      pstring syscmd;
+      pstring outfile;
+         
+      sprintf(outfile,"/tmp/dfree.smb.%d",(int)getpid());
+      sprintf(syscmd,"%s %s",df_command,path);
+      standard_sub_basic(syscmd);
+
+      ret = smbrun(syscmd,outfile);
+      DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));
+         
+      {
+       FILE *f = fopen(outfile,"r");   
+       *dsize = 0;
+       *dfree = 0;
+       *bsize = 1024;
+       if (f)
+         {
+           fscanf(f,"%d %d %d",dsize,dfree,bsize);
+           fclose(f);
+         }
+       else
+         DEBUG(0,("Can't open %s\n",outfile));
+      }
+         
+      unlink(outfile);
+      disk_norm(bsize,dfree,dsize);
+      return(((*bsize)/1024)*(*dfree));
+    }
+
+#ifdef NO_STATFS
+  DEBUG(1,("Warning - no statfs function\n"));
+  return(1);
+#else
+#ifdef STATFS4
+  if (statfs(path,&fs,sizeof(fs),0) != 0)
+#else
+#ifdef USE_STATVFS
+    if (statvfs(path, &fs))
+#else
+#ifdef STATFS3
+      if (statfs(path,&fs,sizeof(fs)) == -1)    
+#else
+       if (statfs(path,&fs) == -1)
+#endif /* STATFS3 */
+#endif /* USE_STATVFS */
+#endif /* STATFS4 */
+         {
+           DEBUG(3,("dfree call failed code errno=%d\n",errno));
+           *bsize = 1024;
+           *dfree = 1;
+           *dsize = 1;
+           return(((*bsize)/1024)*(*dfree));
+         }
+
+#ifdef ULTRIX
+  *bsize = 1024;
+  *dfree = fs.fd_req.bfree;
+  *dsize = fs.fd_req.btot;
+#else
+#ifdef USE_STATVFS
+  *bsize = fs.f_frsize;
+#else
+#ifdef USE_F_FSIZE
+  /* eg: osf1 has f_fsize = fundamental filesystem block size, 
+     f_bsize = optimal transfer block size (MX: 94-04-19) */
+  *bsize = fs.f_fsize;
+#else
+  *bsize = fs.f_bsize;
+#endif /* STATFS3 */
+#endif /* USE_STATVFS */
+
+#ifdef STATFS4
+  *dfree = fs.f_bfree;
+#else
+  *dfree = fs.f_bavail;
+#endif /* STATFS4 */
+  *dsize = fs.f_blocks;
+#endif /* ULTRIX */
+
+#if defined(SCO) || defined(ISC) || defined(MIPS)
+  *bsize = 512;
+#endif
+
+/* handle rediculous bsize values - some OSes are broken */
+if ((*bsize) < 512 || (*bsize)>0xFFFF) *bsize = 1024;
+
+  disk_norm(bsize,dfree,dsize);
+
+  if (*bsize < 256)
+    *bsize = 512;
+  if ((*dsize)<1)
+    {
+      DEBUG(0,("dfree seems to be broken on your system\n"));
+      *dsize = 20*1024*1024/(*bsize);
+      *dfree = MAX(1,*dfree);
+    }
+  return(((*bsize)/1024)*(*dfree));
+#endif
+}
+
+
+/****************************************************************************
+wrap it to get filenames right
+****************************************************************************/
+int sys_disk_free(char *path,int *bsize,int *dfree,int *dsize)
+{
+  return(disk_free(dos_to_unix(path,False),bsize,dfree,dsize));
+}
+
+
+
+/****************************************************************************
+check a filename - possibly caling reducename
+
+This is called by every routine before it allows an operation on a filename.
+It does any final confirmation necessary to ensure that the filename is
+a valid one for the user to access.
+****************************************************************************/
+BOOL check_name(char *name,int cnum)
+{
+  BOOL ret;
+
+  errno = 0;
+
+  ret = reduce_name(name,Connections[cnum].connectpath,lp_widelinks(SNUM(cnum)));
+  if (!ret)
+    DEBUG(5,("check_name on %s failed\n",name));
+
+  return(ret);
+}
+
+/****************************************************************************
+check a filename - possibly caling reducename
+****************************************************************************/
+static void check_for_pipe(char *fname)
+{
+  /* special case of pipe opens */
+  char s[10];
+  StrnCpy(s,fname,9);
+  strlower(s);
+  if (strstr(s,"pipe/"))
+    {
+      DEBUG(3,("Rejecting named pipe open for %s\n",fname));
+      unix_ERR_class = ERRSRV;
+      unix_ERR_code = ERRaccess;
+    }
+}
+
+
+/****************************************************************************
+open a file
+****************************************************************************/
+void open_file(int fnum,int cnum,char *fname1,int flags,int mode)
+{
+  pstring fname;
+
+  Files[fnum].open = False;
+  Files[fnum].fd = -1;
+  errno = EPERM;
+
+  strcpy(fname,fname1);
+
+  /* check permissions */
+  if ((flags != O_RDONLY) && !CAN_WRITE(cnum) && !Connections[cnum].printer)
+    {
+      DEBUG(3,("Permission denied opening %s\n",fname));
+      check_for_pipe(fname);
+      return;
+    }
+
+  /* this handles a bug in Win95 - it doesn't say to create the file when it 
+     should */
+  if (Connections[cnum].printer)
+    flags |= O_CREAT;
+
+/*
+  if (flags == O_WRONLY)
+    DEBUG(3,("Bug in client? Set O_WRONLY without O_CREAT\n"));
+*/
+
+#if UTIME_WORKAROUND
+  /* XXXX - is this OK?? */
+  /* this works around a utime bug but can cause other problems */
+  if ((flags & (O_WRONLY|O_RDWR)) && (flags & O_CREAT) && !(flags & O_APPEND))
+    sys_unlink(fname);
+#endif
+
+
+  Files[fnum].fd = sys_open(fname,flags,mode);
+
+  if ((Files[fnum].fd>=0) && 
+      Connections[cnum].printer && lp_minprintspace(SNUM(cnum))) {
+    pstring dname;
+    int dum1,dum2,dum3;
+    char *p;
+    strcpy(dname,fname);
+    p = strrchr(dname,'/');
+    if (p) *p = 0;
+    if (sys_disk_free(dname,&dum1,&dum2,&dum3) < 
+       lp_minprintspace(SNUM(cnum))) {
+      close(Files[fnum].fd);
+      Files[fnum].fd = -1;
+      sys_unlink(fname);
+      errno = ENOSPC;
+      return;
+    }
+  }
+    
+
+  /* Fix for files ending in '.' */
+  if((Files[fnum].fd == -1) && (errno == ENOENT) && 
+     (strchr(fname,'.')==NULL))
+    {
+      strcat(fname,".");
+      Files[fnum].fd = sys_open(fname,flags,mode);
+    }
+
+#if (defined(ENAMETOOLONG) && defined(HAVE_PATHCONF))
+  if ((Files[fnum].fd == -1) && (errno == ENAMETOOLONG))
+    {
+      int max_len;
+      char *p = strrchr(fname, '/');
+
+      if (p == fname)  /* name is "/xxx" */
+       {
+         max_len = pathconf("/", _PC_NAME_MAX);
+         p++;
+       }
+      else if ((p == NULL) || (p == fname))
+       {
+         p = fname;
+         max_len = pathconf(".", _PC_NAME_MAX);
+       }
+      else
+       {
+         *p = '\0';
+         max_len = pathconf(fname, _PC_NAME_MAX);
+         *p = '/';
+         p++;
+       }
+      if (strlen(p) > max_len)
+       {
+         char tmp = p[max_len];
+
+         p[max_len] = '\0';
+         if ((Files[fnum].fd = sys_open(fname,flags,mode)) == -1)
+           p[max_len] = tmp;
+       }
+    }
+#endif
+
+  if (Files[fnum].fd < 0)
+    {
+      DEBUG(3,("Error opening file %s (%s) (flags=%d)\n",
+              fname,strerror(errno),flags));
+      check_for_pipe(fname);
+      return;
+    }
+
+  if (Files[fnum].fd >= 0)
+    {
+      struct stat st;
+      Connections[cnum].num_files_open++;
+      fstat(Files[fnum].fd,&st);
+      Files[fnum].mode = st.st_mode;
+      Files[fnum].open_time = time(NULL);
+      Files[fnum].size = 0;
+      Files[fnum].pos = -1;
+      Files[fnum].open = True;
+      Files[fnum].mmap_ptr = NULL;
+      Files[fnum].mmap_size = 0;
+      Files[fnum].can_lock = True;
+      Files[fnum].can_read = ((flags & O_WRONLY)==0);
+      Files[fnum].can_write = ((flags & (O_WRONLY|O_RDWR))!=0);
+      Files[fnum].share_mode = 0;
+      Files[fnum].share_pending = False;
+      Files[fnum].print_file = Connections[cnum].printer;
+      Files[fnum].modified = False;
+      Files[fnum].cnum = cnum;
+      string_set(&Files[fnum].name,fname);
+      Files[fnum].wbmpx_ptr = NULL;      
+
+      /*
+       * If the printer is marked as postscript output a leading
+       * file identifier to ensure the file is treated as a raw
+       * postscript file.
+       * This has a similar effect as CtrlD=0 in WIN.INI file.
+       * tim@fsg.com 09/06/94
+       */
+      if (Files[fnum].print_file && POSTSCRIPT(cnum) && 
+         Files[fnum].can_write) 
+       {
+         DEBUG(3,("Writing postscript line\n"));
+         write_file(fnum,"%!\n",3);
+       }
+      
+      DEBUG(2,("%s %s opened file %s read=%s write=%s (numopen=%d fnum=%d)\n",
+              timestring(),Connections[cnum].user,fname,
+              BOOLSTR(Files[fnum].can_read),BOOLSTR(Files[fnum].can_write),
+              Connections[cnum].num_files_open,fnum));
+
+    }
+
+#if USE_MMAP
+  /* mmap it if read-only */
+  if (!Files[fnum].can_write)
+    {
+      Files[fnum].mmap_size = file_size(fname);
+      Files[fnum].mmap_ptr = (char *)mmap(NULL,Files[fnum].mmap_size,
+                                         PROT_READ,MAP_SHARED,Files[fnum].fd,0);
+
+      if (Files[fnum].mmap_ptr == (char *)-1 || !Files[fnum].mmap_ptr)
+       {
+         DEBUG(3,("Failed to mmap() %s - %s\n",fname,strerror(errno)));
+         Files[fnum].mmap_ptr = NULL;
+       }
+    }
+#endif
+}
+
+/*******************************************************************
+sync a file
+********************************************************************/
+void sync_file(int fnum)
+{
+#ifndef NO_FSYNC
+  fsync(Files[fnum].fd);
+#endif
+}
+
+/****************************************************************************
+run a file if it is a magic script
+****************************************************************************/
+static void check_magic(int fnum,int cnum)
+{
+  if (!*lp_magicscript(SNUM(cnum)))
+    return;
+
+  DEBUG(5,("checking magic for %s\n",Files[fnum].name));
+
+  {
+    char *p;
+    if (!(p = strrchr(Files[fnum].name,'/')))
+      p = Files[fnum].name;
+    else
+      p++;
+
+    if (!strequal(lp_magicscript(SNUM(cnum)),p))
+      return;
+  }
+
+  {
+    int ret;
+    pstring magic_output;
+    pstring fname;
+    strcpy(fname,Files[fnum].name);
+
+    if (*lp_magicoutput(SNUM(cnum)))
+      strcpy(magic_output,lp_magicoutput(SNUM(cnum)));
+    else
+      sprintf(magic_output,"%s.out",fname);
+
+    chmod(fname,0755);
+    ret = smbrun(fname,magic_output);
+    DEBUG(3,("Invoking magic command %s gave %d\n",fname,ret));
+    unlink(fname);
+  }
+}
+
+
+/****************************************************************************
+close a file - possibly invalidating the read prediction
+****************************************************************************/
+void close_file(int fnum)
+{
+  int cnum = Files[fnum].cnum;
+  invalidate_read_prediction(Files[fnum].fd);
+  Files[fnum].open = False;
+  Connections[cnum].num_files_open--;
+  if(Files[fnum].wbmpx_ptr) 
+    {
+      free((char *)Files[fnum].wbmpx_ptr);
+      Files[fnum].wbmpx_ptr = NULL;
+    }
+
+#if USE_MMAP
+  if(Files[fnum].mmap_ptr) 
+    {
+      munmap(Files[fnum].mmap_ptr,Files[fnum].mmap_size);
+      Files[fnum].mmap_ptr = NULL;
+    }
+#endif
+
+  if (lp_share_modes(SNUM(cnum)))
+    del_share_mode(fnum);
+
+  if (Files[fnum].modified) {
+    struct stat st;
+    if (fstat(Files[fnum].fd,&st) == 0) {
+      int dosmode = dos_mode(cnum,Files[fnum].name,&st);
+      if (!IS_DOS_ARCHIVE(dosmode)) {  
+       dos_chmod(cnum,Files[fnum].name,dosmode | aARCH,&st);
+      }
+    }    
+  }
+
+  close(Files[fnum].fd);
+
+  /* NT uses smbclose to start a print - weird */
+  if (Files[fnum].print_file)
+    print_file(fnum);
+
+  /* check for magic scripts */
+  check_magic(fnum,cnum);
+
+  DEBUG(2,("%s %s closed file %s (numopen=%d)\n",
+          timestring(),Connections[cnum].user,Files[fnum].name,
+          Connections[cnum].num_files_open));
+}
+
+enum {AFAIL,AREAD,AWRITE,AALL};
+
+/*******************************************************************
+reproduce the share mode access table
+********************************************************************/
+static int access_table(int new_deny,int old_deny,int old_mode,
+                       int share_pid,char *fname)
+{
+  if (new_deny == DENY_ALL || old_deny == DENY_ALL) return(AFAIL);
+
+  if (new_deny == DENY_DOS || old_deny == DENY_DOS) {
+    if (old_deny == new_deny && share_pid == getpid()) 
+       return(AALL);    
+
+    if (old_mode == 0) return(AREAD);
+
+    /* the new smbpub.zip spec says that if the file extension is
+       .com, .dll, .exe or .sym then allow the open. I will force
+       it to read-only as this seems sensible although the spec is
+       a little unclear on this. */
+    if ((fname = strrchr(fname,'.'))) {
+      if (strequal(fname,".com") ||
+         strequal(fname,".dll") ||
+         strequal(fname,".exe") ||
+         strequal(fname,".sym"))
+       return(AREAD);
+    }
+
+    return(AFAIL);
+  }
+
+  switch (new_deny) 
+    {
+    case DENY_WRITE:
+      if (old_deny==DENY_WRITE && old_mode==0) return(AREAD);
+      if (old_deny==DENY_READ && old_mode==0) return(AWRITE);
+      if (old_deny==DENY_NONE && old_mode==0) return(AALL);
+      return(AFAIL);
+    case DENY_READ:
+      if (old_deny==DENY_WRITE && old_mode==1) return(AREAD);
+      if (old_deny==DENY_READ && old_mode==1) return(AWRITE);
+      if (old_deny==DENY_NONE && old_mode==1) return(AALL);
+      return(AFAIL);
+    case DENY_NONE:
+      if (old_deny==DENY_WRITE) return(AREAD);
+      if (old_deny==DENY_READ) return(AWRITE);
+      if (old_deny==DENY_NONE) return(AALL);
+      return(AFAIL);      
+    }
+  return(AFAIL);      
+}
+
+/*******************************************************************
+check if the share mode on a file allows it to be deleted or unlinked
+return True if sharing doesn't prevent the operation
+********************************************************************/
+BOOL check_file_sharing(int cnum,char *fname)
+{
+  int pid=0;
+  int share_mode = get_share_mode_byname(cnum,fname,&pid);
+
+  if (!pid || !share_mode) return(True);
+  if (share_mode == DENY_DOS)
+    return(pid == getpid());
+
+  /* XXXX exactly what share mode combinations should be allowed for
+     deleting/renaming? */
+  return(False);
+}
+
+/****************************************************************************
+  C. Hoch 11/22/95
+  Helper for open_file_shared. 
+  Truncate a file after checking locking; close file if locked.
+  **************************************************************************/
+static void truncate_unless_locked(int fnum, int cnum)
+{
+  if (Files[fnum].can_write){
+    if (is_locked(fnum,cnum,0x3FFFFFFF,0)){
+      close_file(fnum);   
+      errno = EACCES;
+      unix_ERR_class = ERRDOS;
+      unix_ERR_code = ERRlock;
+    }
+    else
+      ftruncate(Files[fnum].fd,0); 
+  }
+}
+
+
+/****************************************************************************
+open a file with a share mode
+****************************************************************************/
+void open_file_shared(int fnum,int cnum,char *fname,int share_mode,int ofun,
+                     int mode,int *Access,int *action)
+{
+  int flags=0;
+  int flags2=0;
+  int deny_mode = (share_mode>>4)&7;
+  struct stat sbuf;
+  BOOL file_existed = file_exist(fname,&sbuf);
+  BOOL fcbopen = False;
+  int share_pid=0;
+
+  Files[fnum].open = False;
+  Files[fnum].fd = -1;
+
+  /* this is for OS/2 EAs - try and say we don't support them */
+  if (strstr(fname,".+,;=[].")) {
+    unix_ERR_class = ERRDOS;
+    unix_ERR_code = ERROR_EAS_NOT_SUPPORTED;
+    return;
+  }
+
+  if ((ofun & 0x3) == 0 && file_existed) {
+    errno = EEXIST;
+    return;
+  }
+      
+  if (ofun & 0x10)
+    flags2 |= O_CREAT;
+  if ((ofun & 0x3) == 2)
+    flags2 |= O_TRUNC;
+
+  /* note that we ignore the append flag as 
+     append does not mean the same thing under dos and unix */
+
+  switch (share_mode&0xF)
+    {
+    case 1: 
+      flags = O_WRONLY; 
+      break;
+    case 0xF: 
+      fcbopen = True;
+      flags = O_RDWR; 
+      break;
+    case 2: 
+      flags = O_RDWR; 
+      break;
+    default:
+      flags = O_RDONLY;
+      break;
+    }
+  
+  if (flags != O_RDONLY && file_existed && 
+      (!CAN_WRITE(cnum) || IS_DOS_READONLY(dos_mode(cnum,fname,&sbuf)))) {
+    if (!fcbopen) {
+      errno = EACCES;
+      return;
+    }
+    flags = O_RDONLY;
+  }
+
+  if (deny_mode > DENY_NONE && deny_mode!=DENY_FCB) {
+    DEBUG(2,("Invalid deny mode %d on file %s\n",deny_mode,fname));
+    errno = EINVAL;
+    return;
+  }
+
+  if (deny_mode == DENY_FCB) deny_mode = DENY_DOS;
+
+  if (lp_share_modes(SNUM(cnum))) {
+    int old_share=0;
+
+    if (file_existed)
+      old_share = get_share_mode(cnum,&sbuf,&share_pid);
+
+    if (share_pid) {
+      /* someone else has a share lock on it, check to see 
+        if we can too */
+      int old_open_mode = old_share&0xF;
+      int old_deny_mode = (old_share>>4)&7;
+
+      if (deny_mode > 4 || old_deny_mode > 4 || old_open_mode > 2) {
+       DEBUG(2,("Invalid share mode (%d,%d,%d) on file %s\n",
+                deny_mode,old_deny_mode,old_open_mode,fname));
+       errno = EACCES;
+       unix_ERR_class = ERRDOS;
+       unix_ERR_code = ERRbadshare;
+       return;
+      }
+
+      {
+       int access_allowed = access_table(deny_mode,old_deny_mode,old_open_mode,
+                                         share_pid,fname);
+
+       if ((access_allowed == AFAIL) ||
+           (access_allowed == AREAD && flags == O_WRONLY) ||
+           (access_allowed == AWRITE && flags == O_RDONLY)) {
+         DEBUG(2,("Share violation on file (%d,%d,%d,%d,%s) = %d\n",
+                  deny_mode,old_deny_mode,old_open_mode,
+                  share_pid,fname,
+                  access_allowed));
+         errno = EACCES;
+         unix_ERR_class = ERRDOS;
+         unix_ERR_code = ERRbadshare;
+         return;
+       }
+       
+       if (access_allowed == AREAD)
+         flags = O_RDONLY;
+       
+       if (access_allowed == AWRITE)
+         flags = O_WRONLY;
+      }
+    }
+  }
+
+  DEBUG(4,("calling open_file with flags=0x%X flags2=0x%X mode=0%o\n",
+          flags,flags2,mode));
+
+  open_file(fnum,cnum,fname,flags|(flags2&~(O_TRUNC)),mode);
+  if (!Files[fnum].open && flags==O_RDWR && errno!=ENOENT && fcbopen) {
+    flags = O_RDONLY;
+    open_file(fnum,cnum,fname,flags,mode);
+  }
+
+  if (Files[fnum].open) {
+    int open_mode=0;
+    switch (flags) {
+    case O_RDONLY:
+      open_mode = 0;
+      break;
+    case O_RDWR:
+      open_mode = 2;
+      break;
+    case O_WRONLY:
+      open_mode = 1;
+      break;
+    }
+
+    Files[fnum].share_mode = (deny_mode<<4) | open_mode;
+    Files[fnum].share_pending = True;
+
+    if (Access) {
+      (*Access) = open_mode;
+    }
+    
+    if (action) {
+      if (file_existed && !(flags2 & O_TRUNC)) *action = 1;
+      if (!file_existed) *action = 2;
+      if (file_existed && (flags2 & O_TRUNC)) *action = 3;
+    }
+
+    if (!share_pid)
+      share_mode_pending = True;
+
+    if ((flags2&O_TRUNC) && file_existed)
+      truncate_unless_locked(fnum,cnum);
+  }
+}
+
+
+
+/*******************************************************************
+check for files that we should now set our share modes on
+********************************************************************/
+static void check_share_modes(void)
+{
+  int i;
+  for (i=0;i<MAX_OPEN_FILES;i++)
+    if(Files[i].open && Files[i].share_pending) {
+      if (lp_share_modes(SNUM(Files[i].cnum))) {
+       int pid=0;
+       get_share_mode_by_fnum(Files[i].cnum,i,&pid);
+       if (!pid) {
+         set_share_mode(i,Files[i].share_mode);
+         Files[i].share_pending = False;
+       }
+      } else {
+       Files[i].share_pending = False; 
+      }
+    }
+}
+
+
+/****************************************************************************
+seek a file. Try to avoid the seek if possible
+****************************************************************************/
+int seek_file(int fnum,int pos)
+{
+  int offset = 0;
+  if (Files[fnum].print_file && POSTSCRIPT(Files[fnum].cnum))
+    offset = 3;
+
+  Files[fnum].pos = lseek(Files[fnum].fd,pos+offset,SEEK_SET) - offset;
+  return(Files[fnum].pos);
+}
+
+/****************************************************************************
+read from a file
+****************************************************************************/
+int read_file(int fnum,char *data,int pos,int mincnt,int maxcnt,int timeout,BOOL exact)
+{
+  int ret=0;
+
+  if (!Files[fnum].can_write)
+    {
+      ret = read_predict(Files[fnum].fd,
+                        pos,
+                        data,
+                        NULL,
+                        maxcnt);
+
+      data += ret;
+      maxcnt -= ret;
+      mincnt = MAX(mincnt-ret,0);
+      pos += ret;
+    }
+
+#if USE_MMAP
+  if (Files[fnum].mmap_ptr)
+    {
+      int num = MIN(maxcnt,Files[fnum].mmap_size-pos);
+      if (num > 0)
+       {
+         memcpy(data,Files[fnum].mmap_ptr+pos,num);
+         data += num;
+         pos += num;
+         maxcnt -= num;
+         mincnt = MAX(mincnt-num,0);
+         ret += num;
+       }
+    }
+#endif
+
+  if (maxcnt <= 0)
+    return(ret);
+
+  if (seek_file(fnum,pos) != pos)
+    {
+      DEBUG(3,("Failed to seek to %d\n",pos));
+      return(ret);
+    }
+  
+  if (maxcnt > 0)
+    ret += read_with_timeout(Files[fnum].fd,
+                            data,
+                            mincnt,
+                            maxcnt,
+                            timeout,
+                            exact);
+
+  return(ret);
+}
+
+
+/****************************************************************************
+write to a file
+****************************************************************************/
+int write_file(int fnum,char *data,int n)
+{
+  if (!Files[fnum].can_write) {
+    errno = EPERM;
+    return(0);
+  }
+
+  Files[fnum].modified = True;
+
+  return(write_data(Files[fnum].fd,data,n));
+}
+
+
+static int old_umask = 022;
+
+/****************************************************************************
+load parameters specific to a connection/service
+****************************************************************************/
+BOOL become_service(int cnum,BOOL do_chdir)
+{
+  extern char magic_char;
+  static int last_cnum = -1;
+  int snum;
+
+  if (!OPEN_CNUM(cnum))
+    {
+      last_cnum = -1;
+      return(False);
+    }
+
+  Connections[cnum].lastused = smb_last_time;
+
+  snum = SNUM(cnum);
+  
+  if (do_chdir &&
+      ChDir(Connections[cnum].connectpath) != 0 &&
+      ChDir(Connections[cnum].origpath) != 0)
+    {
+      DEBUG(0,("%s chdir (%s) failed cnum=%d\n",timestring(),
+           Connections[cnum].connectpath,cnum));     
+      return(False);
+    }
+
+  if (cnum == last_cnum)
+    return(True);
+
+  last_cnum = cnum;
+
+  case_default = lp_defaultcase(snum);
+  case_preserve = lp_preservecase(snum);
+  short_case_preserve = lp_shortpreservecase(snum);
+  case_mangle = lp_casemangle(snum);
+  case_sensitive = lp_casesensitive(snum);
+  magic_char = lp_magicchar(snum);
+  use_mangled_map = (*lp_mangled_map(snum) ? True:False);
+  return(True);
+}
+
+
+/****************************************************************************
+  become the specified uid 
+****************************************************************************/
+static BOOL become_uid(int uid)
+{
+  if (initial_uid != 0)
+    return(True);
+
+#ifdef AIX
+  {
+    /* AIX 3 stuff - inspired by a code fragment in wu-ftpd */
+    priv_t priv;
+
+    priv.pv_priv[0] = 0;
+    priv.pv_priv[1] = 0;
+    if (setpriv(PRIV_SET|PRIV_INHERITED|PRIV_EFFECTIVE|PRIV_BEQUEATH,
+               &priv, sizeof(priv_t)) < 0 ||
+       setuidx(ID_REAL|ID_EFFECTIVE, (uid_t)uid) < 0 ||
+       seteuid((uid_t)uid) < 0) 
+      DEBUG(1,("Can't set uid (AIX3)"));
+  }
+#endif
+
+#ifdef USE_SETRES
+  if (setresuid(-1,uid,-1) != 0)
+#else
+    if ((seteuid(uid) != 0) && 
+       (setuid(uid) != 0))
+#endif
+      {
+       DEBUG(0,("Couldn't set uid %d currently set to (%d,%d)\n",
+                uid,getuid(), geteuid()));
+       if (uid > 32000)
+         DEBUG(0,("Looks like your OS doesn't like high uid values - try using a different account\n"));
+       return(False);
+      }
+
+  if (((uid == -1) || (uid == 65535)) && geteuid() != uid)
+    {
+      DEBUG(0,("Invalid uid -1. perhaps you have a account with uid 65535?\n"));
+      return(False);
+    }
+
+  return(True);
+}
+
+
+/****************************************************************************
+  become the specified gid
+****************************************************************************/
+static BOOL become_gid(int gid)
+{
+  if (initial_uid != 0)
+    return(True);
+  
+#ifdef USE_SETRES 
+  if (setresgid(-1,gid,-1) != 0)
+#else
+    if (setgid(gid) != 0)
+#endif
+      {
+       DEBUG(0,("Couldn't set gid %d currently set to (%d,%d)\n",
+                gid,getgid(),getegid()));
+       if (gid > 32000)
+         DEBUG(0,("Looks like your OS doesn't like high gid values - try using a different account\n"));
+       return(False);
+      }
+
+  return(True);
+}
+
+
+/****************************************************************************
+  become the specified uid and gid
+****************************************************************************/
+static BOOL become_id(int uid,int gid)
+{
+  return(become_gid(gid) && become_uid(uid));
+}
+
+/****************************************************************************
+become the guest user
+****************************************************************************/
+static BOOL become_guest(void)
+{
+  BOOL ret;
+  static struct passwd *pass=NULL;
+
+  if (initial_uid != 0) 
+    return(True);
+
+  if (!pass)
+    pass = Get_Pwnam(lp_guestaccount(-1),True);
+  if (!pass) return(False);
+
+  ret = become_id(pass->pw_uid,pass->pw_gid);
+
+  if (!ret)
+    DEBUG(1,("Failed to become guest. Invalid guest account?\n"));
+
+  last_user.cnum = -2;
+
+  return(ret);
+}
+
+/*******************************************************************
+check if a username is OK
+********************************************************************/
+static BOOL check_user_ok(int cnum,user_struct *vuser,int snum)
+{
+  int i;
+  for (i=0;i<Connections[cnum].uid_cache.entries;i++)
+    if (Connections[cnum].uid_cache.list[i] == vuser->uid) return(True);
+
+  if (!user_ok(vuser->name,snum)) return(False);
+
+  i = Connections[cnum].uid_cache.entries % UID_CACHE_SIZE;
+  Connections[cnum].uid_cache.list[i] = vuser->uid;
+
+  if (Connections[cnum].uid_cache.entries < UID_CACHE_SIZE)
+    Connections[cnum].uid_cache.entries++;
+
+  return(True);
+}
+
+
+/****************************************************************************
+  become the user of a connection number
+****************************************************************************/
+BOOL become_user(int cnum, int uid)
+{
+  int new_umask;
+  user_struct *vuser;
+  int snum,gid;
+  int ngroups;
+  gid_t *groups;
+
+  if (last_user.cnum == cnum && last_user.uid == uid) {
+    DEBUG(4,("Skipping become_user - already user\n"));
+    return(True);
+  }
+
+  unbecome_user();
+
+  if (!OPEN_CNUM(cnum)) {
+    DEBUG(2,("Connection %d not open\n",cnum));
+    return(False);
+  }
+
+  snum = Connections[cnum].service;
+
+  if (Connections[cnum].force_user || 
+      lp_security() == SEC_SHARE ||
+      !(vuser = get_valid_user_struct(uid)) ||
+      !check_user_ok(cnum,vuser,snum)) {
+    uid = Connections[cnum].uid;
+    gid = Connections[cnum].gid;
+    groups = Connections[cnum].groups;
+    ngroups = Connections[cnum].ngroups;
+  } else {
+    if (!vuser) {
+      DEBUG(2,("Invalid vuid used %d\n",uid));
+      return(False);
+    }
+    uid = vuser->uid;
+    if(!*lp_force_group(snum))
+      gid = vuser->gid;
+    else
+      gid = Connections[cnum].gid;
+    groups = vuser->user_groups;
+    ngroups = vuser->user_ngroups;
+  }
+
+  if (initial_uid == 0)
+    {
+      if (!become_gid(gid)) return(False);
+
+#ifndef NO_SETGROUPS      
+      if (!IS_IPC(cnum)) {
+       /* groups stuff added by ih/wreu */
+       if (ngroups > 0)
+         if (setgroups(ngroups,groups)<0)
+           DEBUG(0,("setgroups call failed!\n"));
+      }
+#endif
+
+      if (!Connections[cnum].admin_user && !become_uid(uid))
+       return(False);
+    }
+
+  new_umask = 0777 & ~CREATE_MODE(cnum);
+  old_umask = umask(new_umask);
+
+  last_user.cnum = cnum;
+  last_user.uid = uid;
+  
+  DEBUG(5,("become_user uid=(%d,%d) gid=(%d,%d) new_umask=0%o\n",
+          getuid(),geteuid(),getgid(),getegid(),new_umask));
+  
+  return(True);
+}
+
+/****************************************************************************
+  unbecome the user of a connection number
+****************************************************************************/
+BOOL unbecome_user(void )
+{
+  if (last_user.cnum == -1)
+    return(False);
+
+  ChDir(OriginalDir);
+
+  umask(old_umask);
+
+  if (initial_uid == 0)
+    {
+#ifdef USE_SETRES
+      setresuid(-1,getuid(),-1);
+      setresgid(-1,getgid(),-1);
+#else
+      if (seteuid(initial_uid) != 0) 
+       setuid(initial_uid);
+      setgid(initial_gid);
+#endif
+    }
+#ifdef NO_EID
+  if (initial_uid == 0)
+    DEBUG(2,("Running with no EID\n"));
+  initial_uid = getuid();
+  initial_gid = getgid();
+#else
+  if (geteuid() != initial_uid)
+    {
+      DEBUG(0,("Warning: You appear to have a trapdoor uid system\n"));
+      initial_uid = geteuid();
+    }
+  if (getegid() != initial_gid)
+    {
+      DEBUG(0,("Warning: You appear to have a trapdoor gid system\n"));
+      initial_gid = getegid();
+    }
+#endif
+  
+  if (ChDir(OriginalDir) != 0)
+    DEBUG(0,("%s chdir(%s) failed in unbecome_user\n",
+            timestring(),OriginalDir));  
+
+  DEBUG(5,("unbecome_user now uid=(%d,%d) gid=(%d,%d)\n",
+       getuid(),geteuid(),getgid(),getegid()));
+
+  last_user.cnum = -1;
+
+  return(True);
+}
+
+/****************************************************************************
+  find a service entry
+****************************************************************************/
+int find_service(char *service)
+{
+   int iService;
+
+   string_sub(service,"\\","/");
+
+   iService = lp_servicenumber(service);
+
+   /* now handle the special case of a home directory */
+   if (iService < 0)
+   {
+      char *phome_dir = get_home_dir(service);
+      DEBUG(3,("checking for home directory %s gave %s\n",service,
+           phome_dir?phome_dir:"(NULL)"));
+      if (phome_dir)
+      {   
+        int iHomeService;
+        if ((iHomeService = lp_servicenumber(HOMES_NAME)) >= 0)
+        {
+           lp_add_home(service,iHomeService,phome_dir);
+           iService = lp_servicenumber(service);
+        }
+      }
+   }
+
+   /* If we still don't have a service, attempt to add it as a printer. */
+   if (iService < 0)
+   {
+      int iPrinterService;
+
+      if ((iPrinterService = lp_servicenumber(PRINTERS_NAME)) >= 0)
+      {
+         char *pszTemp;
+
+         DEBUG(3,("checking whether %s is a valid printer name...\n", service));
+         pszTemp = PRINTCAP;
+         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));
+      }
+   }
+
+   /* just possibly it's a default service? */
+   if (iService < 0) 
+     {
+       char *defservice = lp_defaultservice();
+       if (defservice && *defservice && !strequal(defservice,service)) {
+        iService = find_service(defservice);
+        if (iService >= 0) {
+          string_sub(service,"_","/");
+          iService = lp_add_service(service,iService);
+        }
+       }
+     }
+
+   if (iService >= 0)
+      if (!VALID_SNUM(iService))
+      {
+         DEBUG(0,("Invalid snum %d for %s\n",iService,service));
+        iService = -1;
+      }
+
+   if (iService < 0)
+      DEBUG(3,("find_service() failed to find service %s\n", service));
+
+   return (iService);
+}
+
+
+/****************************************************************************
+  create an error packet from a cached error.
+****************************************************************************/
+int cached_error_packet(char *inbuf,char *outbuf,int fnum,int line)
+{
+  write_bmpx_struct *wbmpx = Files[fnum].wbmpx_ptr;
+
+  int32 eclass = wbmpx->wr_errclass;
+  int32 err = wbmpx->wr_error;
+
+  /* We can now delete the auxiliary struct */
+  free((char *)wbmpx);
+  Files[fnum].wbmpx_ptr = NULL;
+  return error_packet(inbuf,outbuf,eclass,err,line);
+}
+
+
+struct
+{
+  int unixerror;
+  int smbclass;
+  int smbcode;
+} unix_smb_errmap[] =
+{
+  {EPERM,ERRDOS,ERRnoaccess},
+  {EACCES,ERRDOS,ERRnoaccess},
+  {ENOENT,ERRDOS,ERRbadfile},
+  {EIO,ERRHRD,ERRgeneral},
+  {EBADF,ERRSRV,ERRsrverror},
+  {EINVAL,ERRSRV,ERRsrverror},
+  {EEXIST,ERRDOS,ERRfilexists},
+  {ENFILE,ERRDOS,ERRnofids},
+  {EMFILE,ERRDOS,ERRnofids},
+  {ENOSPC,ERRHRD,ERRdiskfull},
+#ifdef EDQUOT
+  {EDQUOT,ERRHRD,ERRdiskfull},
+#endif
+#ifdef ENOTEMPTY
+  {ENOTEMPTY,ERRDOS,ERRnoaccess},
+#endif
+#ifdef EXDEV
+  {EXDEV,ERRDOS,ERRdiffdevice},
+#endif
+  {EROFS,ERRHRD,ERRnowrite},
+  {0,0,0}
+};
+
+
+/****************************************************************************
+  create an error packet from errno
+****************************************************************************/
+int unix_error_packet(char *inbuf,char *outbuf,int def_class,uint32 def_code,int line)
+{
+  int eclass=def_class;
+  int ecode=def_code;
+  int i=0;
+
+  if (unix_ERR_class != SUCCESS)
+    {
+      eclass = unix_ERR_class;
+      ecode = unix_ERR_code;
+      unix_ERR_class = SUCCESS;
+      unix_ERR_code = 0;
+    }
+  else
+    {
+      while (unix_smb_errmap[i].smbclass != 0)
+       {
+         if (unix_smb_errmap[i].unixerror == errno)
+           {
+             eclass = unix_smb_errmap[i].smbclass;
+             ecode = unix_smb_errmap[i].smbcode;
+             break;
+           }
+         i++;
+       }
+    }
+
+  return(error_packet(inbuf,outbuf,eclass,ecode,line));
+}
+
+
+/****************************************************************************
+  create an error packet. Normally called using the ERROR() macro
+****************************************************************************/
+int error_packet(char *inbuf,char *outbuf,int error_class,uint32 error_code,int line)
+{
+  int outsize = set_message(outbuf,0,0,True);
+  int cmd;
+  cmd = CVAL(inbuf,smb_com);
+  
+  CVAL(outbuf,smb_rcls) = error_class;
+  SSVAL(outbuf,smb_err,error_code);  
+  
+  DEBUG(3,("%s error packet at line %d cmd=%d (%s) eclass=%d ecode=%d\n",
+          timestring(),
+          line,
+          (int)CVAL(inbuf,smb_com),
+          smb_fn_name(CVAL(inbuf,smb_com)),
+          error_class,
+          error_code));
+
+  if (errno != 0)
+    DEBUG(3,("error string = %s\n",strerror(errno)));
+  
+  return(outsize);
+}
+
+
+#ifndef SIGCLD_IGNORE
+/****************************************************************************
+this prevents zombie child processes
+****************************************************************************/
+static int sig_cld()
+{
+  static int depth = 0;
+  if (depth != 0)
+    {
+      DEBUG(0,("ERROR: Recursion in sig_cld? Perhaps you need `#define USE_WAITPID'?\n"));
+      depth=0;
+      return(0);
+    }
+  depth++;
+
+  BlockSignals(True);
+  DEBUG(5,("got SIGCLD\n"));
+
+#ifdef USE_WAITPID
+  while (waitpid((pid_t)-1,(int *)NULL, WNOHANG) > 0);
+#endif
+
+  /* Stop zombies */
+  /* Stevens, Adv. Unix Prog. says that on system V you must call
+     wait before reinstalling the signal handler, because the kernel
+     calls the handler from within the signal-call when there is a
+     child that has exited. This would lead to an infinite recursion
+     if done vice versa. */
+        
+#ifndef DONT_REINSTALL_SIG
+#ifdef SIGCLD_IGNORE
+  signal(SIGCLD, SIG_IGN);  
+#else
+  signal(SIGCLD, SIGNAL_CAST sig_cld);
+#endif
+#endif
+
+#ifndef USE_WAITPID
+  while (wait3(WAIT3_CAST1 NULL, WNOHANG, WAIT3_CAST2 NULL) > 0);
+#endif
+  depth--;
+  BlockSignals(False);
+  return 0;
+}
+#endif
+
+/****************************************************************************
+  this is called when the client exits abruptly
+  **************************************************************************/
+static int sig_pipe()
+{
+  exit_server("Got sigpipe\n");
+  return(0);
+}
+
+/****************************************************************************
+  open the socket communication
+****************************************************************************/
+static BOOL open_sockets(BOOL is_daemon,int port)
+{
+  extern int Client;
+
+  if (is_daemon)
+    {
+      int s;
+      struct sockaddr addr;
+      int in_addrlen = sizeof(addr);
+       
+      /* Stop zombies */
+#ifdef SIGCLD_IGNORE
+      signal(SIGCLD, SIG_IGN);
+#else
+      signal(SIGCLD, SIGNAL_CAST sig_cld);
+#endif
+
+      /* open an incoming socket */
+      s = open_socket_in(SOCK_STREAM, port, 0);
+      if (s == -1)
+       return(False);
+
+      /* ready to listen */
+      if (listen(s, 5) == -1) 
+       {
+         DEBUG(0,("listen: %s",strerror(errno)));
+         close(s);
+         return False;
+       }
+      
+      /* now accept incoming connections - forking a new process
+        for each incoming connection */
+      DEBUG(2,("waiting for a connection\n"));
+      while (1)
+       {
+         Client = accept(s,&addr,&in_addrlen);
+
+         if (Client == -1 && errno == EINTR)
+           continue;
+
+         if (Client == -1)
+           {
+             DEBUG(0,("accept: %s",strerror(errno)));
+             return False;
+           }
+
+#ifdef NO_FORK_DEBUG
+#ifndef NO_SIGNAL_TEST
+          signal(SIGPIPE, SIGNAL_CAST sig_pipe);
+          signal(SIGCLD, SIGNAL_CAST SIG_DFL);
+#endif
+         return True;
+#else
+         if (Client != -1 && fork()==0)
+           {
+#ifndef NO_SIGNAL_TEST
+             signal(SIGPIPE, SIGNAL_CAST sig_pipe);
+             signal(SIGCLD, SIGNAL_CAST SIG_DFL);
+#endif
+             /* close our standard file descriptors */
+             close_low_fds();
+  
+             set_socket_options(Client,"SO_KEEPALIVE");
+             set_socket_options(Client,user_socket_options);
+
+             return True; 
+           }
+          close(Client); /* The parent doesn't need this socket */
+#endif
+       }
+    }
+  else
+    {
+      /* We will abort gracefully when the client or remote system 
+        goes away */
+#ifndef NO_SIGNAL_TEST
+      signal(SIGPIPE, SIGNAL_CAST sig_pipe);
+#endif
+      Client = dup(0);
+
+      /* close our standard file descriptors */
+      close_low_fds();
+
+      set_socket_options(Client,"SO_KEEPALIVE");
+      set_socket_options(Client,user_socket_options);
+    }
+
+  return True;
+}
+
+
+/****************************************************************************
+check if a snum is in use
+****************************************************************************/
+BOOL snum_used(int snum)
+{
+  int i;
+  for (i=0;i<MAX_CONNECTIONS;i++)
+    if (OPEN_CNUM(i) && (SNUM(i) == snum))
+      return(True);
+  return(False);
+}
+
+/****************************************************************************
+  reload the services file
+  **************************************************************************/
+BOOL reload_services(BOOL test)
+{
+  BOOL ret;
+
+  if (lp_loaded())
+    {
+      pstring fname;
+      strcpy(fname,lp_configfile());
+      if (file_exist(fname,NULL) && !strcsequal(fname,servicesf))
+       {
+         strcpy(servicesf,fname);
+         test = False;
+       }
+    }
+
+  reopen_logs();
+
+  if (test && !lp_file_list_changed())
+    return(True);
+
+  lp_killunused(snum_used);
+
+  ret = lp_load(servicesf,False);
+
+  /* perhaps the config filename is now set */
+  if (!test)
+    reload_services(True);
+
+  reopen_logs();
+
+  {
+    extern int Client;
+    if (Client != -1) {      
+      set_socket_options(Client,"SO_KEEPALIVE");
+      set_socket_options(Client,user_socket_options);
+    }
+  }
+
+  create_mangled_stack(lp_mangledstack());
+
+  /* this forces service parameters to be flushed */
+  become_service(-1,True);
+
+  return(ret);
+}
+
+
+
+/****************************************************************************
+this prevents zombie child processes
+****************************************************************************/
+static int sig_hup()
+{
+  BlockSignals(True);
+  DEBUG(0,("Got SIGHUP\n"));
+  reload_services(False);
+#ifndef DONT_REINSTALL_SIG
+  signal(SIGHUP,SIGNAL_CAST sig_hup);
+#endif
+  BlockSignals(False);
+  return(0);
+}
+
+/****************************************************************************
+Setup the groups a user belongs to.
+****************************************************************************/
+int setup_groups(char *user, int uid, int gid, int *p_ngroups, 
+                int **p_igroups, gid_t **p_groups)
+{
+  if (-1 == initgroups(user,gid))
+    {
+      if (getuid() == 0)
+       {
+         DEBUG(0,("Unable to initgroups!\n"));
+         if (gid < 0 || gid > 16000 || uid < 0 || uid > 16000)
+           DEBUG(0,("This is probably a problem with the account %s\n",user));
+       }
+    }
+  else
+    {
+      int i,ngroups;
+      int *igroups;
+      gid_t grp = 0;
+      ngroups = getgroups(0,&grp);
+      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;
+
+      *p_ngroups = ngroups;
+
+      /* The following bit of code is very strange. It is due to the
+         fact that some OSes use int* and some use gid_t* for
+         getgroups, and some (like SunOS) use both, one in prototypes,
+         and one in man pages and the actual code. Thus we detect it
+         dynamically using some very ugly code */
+      if (ngroups > 0)
+        {
+         /* does getgroups return ints or gid_t ?? */
+         static BOOL groups_use_ints = True;
+
+         if (groups_use_ints && 
+             ngroups == 1 && 
+             SVAL(igroups,2) == 0x4242)
+           groups_use_ints = False;
+         
+          for (i=0;groups_use_ints && i<ngroups;i++)
+            if (igroups[i] == 0x42424242)
+             groups_use_ints = False;
+             
+          if (groups_use_ints)
+            {
+             *p_igroups = igroups;
+             *p_groups = (gid_t *)igroups;       
+            }
+          else
+            {
+             gid_t *groups = (gid_t *)igroups;
+             igroups = (int *)malloc(sizeof(int)*ngroups);
+             for (i=0;i<ngroups;i++)
+               igroups[i] = groups[i];
+             *p_igroups = igroups;
+             *p_groups = (gid_t *)groups;
+           }
+       }
+      DEBUG(3,("%s is in %d groups\n",user,ngroups));
+      for (i=0;i<ngroups;i++)
+        DEBUG(3,("%d ",igroups[i]));
+      DEBUG(3,("\n"));
+    }
+  return 0;
+}
+
+/****************************************************************************
+  make a connection to a service
+****************************************************************************/
+int make_connection(char *service,char *user,char *password, int pwlen, char *dev,int vuid)
+{
+  int cnum;
+  int snum;
+  struct passwd *pass = NULL;
+  connection_struct *pcon;
+  BOOL guest = False;
+  BOOL force = False;
+  static BOOL first_connection = True;
+
+  strlower(service);
+
+  snum = find_service(service);
+  if (snum < 0)
+    {
+      if (strequal(service,"IPC$"))
+       {         
+         DEBUG(3,("%s refusing IPC connection\n",timestring()));
+         return(-3);
+       }
+
+      DEBUG(0,("%s couldn't find service %s\n",timestring(),service));      
+      return(-2);
+    }
+
+  if (strequal(service,HOMES_NAME))
+    {
+      if (*user && Get_Pwnam(user,True))
+       return(make_connection(user,user,password,pwlen,dev,vuid));
+
+      if (validated_username(vuid))
+       {
+         strcpy(user,validated_username(vuid));
+         return(make_connection(user,user,password,pwlen,dev,vuid));
+       }
+    }
+
+  if (!lp_snum_ok(snum) || !check_access(snum)) {    
+    return(-4);
+  }
+
+  /* you can only connect to the IPC$ service as an ipc device */
+  if (strequal(service,"IPC$"))
+    strcpy(dev,"IPC");
+
+  if (*dev == '?' || !*dev)
+    {
+      if (lp_print_ok(snum))
+       strcpy(dev,"LPT1:");
+      else
+       strcpy(dev,"A:");
+    }
+
+  /* if the request is as a printer and you can't print then refuse */
+  strupper(dev);
+  if (!lp_print_ok(snum) && (strncmp(dev,"LPT",3) == 0)) {
+    DEBUG(1,("Attempt to connect to non-printer as a printer\n"));
+    return(-6);
+  }
+
+  /* lowercase the user name */
+  strlower(user);
+
+  /* add it as a possible user name */
+  add_session_user(service);
+
+  /* shall we let them in? */
+  if (!authorise_login(snum,user,password,pwlen,&guest,&force,vuid))
+    {
+      DEBUG(2,("%s invalid username/password for %s\n",timestring(),service));
+      return(-1);
+    }
+  
+  cnum = find_free_connection(str_checksum(service) + str_checksum(user));
+  if (cnum < 0)
+    {
+      DEBUG(0,("%s couldn't find free connection\n",timestring()));      
+      return(-1);
+    }
+
+  pcon = &Connections[cnum];
+  bzero((char *)pcon,sizeof(*pcon));
+
+  /* find out some info about the user */
+  pass = Get_Pwnam(user,True);
+
+  if (pass == NULL)
+    {
+      DEBUG(0,("%s couldn't find account %s\n",timestring(),user)); 
+      return(-7);
+    }
+
+  pcon->read_only = lp_readonly(snum);
+
+  {
+    pstring list;
+    StrnCpy(list,lp_readlist(snum),sizeof(pstring)-1);
+    string_sub(list,"%S",service);
+
+    if (user_in_list(user,list))
+      pcon->read_only = True;
+
+    StrnCpy(list,lp_writelist(snum),sizeof(pstring)-1);
+    string_sub(list,"%S",service);
+
+    if (user_in_list(user,list))
+      pcon->read_only = False;    
+  }
+
+  /* admin user check */
+  if (user_in_list(user,lp_admin_users(snum)) &&
+      !pcon->read_only)
+    {
+      pcon->admin_user = True;
+      DEBUG(0,("%s logged in as admin user (root privileges)\n",user));
+    }
+  else
+    pcon->admin_user = False;
+    
+  pcon->force_user = force;
+  pcon->uid = pass->pw_uid;
+  pcon->gid = pass->pw_gid;
+  pcon->num_files_open = 0;
+  pcon->lastused = time(NULL);
+  pcon->service = snum;
+  pcon->used = True;
+  pcon->printer = (strncmp(dev,"LPT",3) == 0);
+  pcon->ipc = (strncmp(dev,"IPC",3) == 0);
+  pcon->dirptr = NULL;
+  string_set(&pcon->dirpath,"");
+  string_set(&pcon->user,user);
+
+#if HAVE_GETGRNAM 
+  if (*lp_force_group(snum))
+    {
+      struct group *gptr = (struct group *)getgrnam(lp_force_group(snum));
+      if (gptr)
+       {
+         pcon->gid = gptr->gr_gid;
+         DEBUG(3,("Forced group %s\n",lp_force_group(snum)));
+       }
+      else
+       DEBUG(1,("Couldn't find group %s\n",lp_force_group(snum)));
+    }
+#endif
+
+  if (*lp_force_user(snum))
+    {
+      struct passwd *pass2;
+      fstring fuser;
+      strcpy(fuser,lp_force_user(snum));
+      pass2 = (struct passwd *)Get_Pwnam(fuser,True);
+      if (pass2)
+       {
+         pcon->uid = pass2->pw_uid;
+         string_set(&pcon->user,fuser);
+         strcpy(user,fuser);
+         pcon->force_user = True;
+         DEBUG(3,("Forced user %s\n",fuser));    
+       }
+      else
+       DEBUG(1,("Couldn't find user %s\n",fuser));
+    }
+
+  {
+    pstring s;
+    strcpy(s,lp_pathname(snum));
+    standard_sub(cnum,s);
+    string_set(&pcon->connectpath,s);
+    DEBUG(3,("Connect path is %s\n",s));
+  }
+
+  /* groups stuff added by ih */
+  pcon->ngroups = 0;
+  pcon->groups = NULL;
+
+  if (!IS_IPC(cnum))
+    {
+      /* Find all the groups this uid is in and store them. Used by become_user() */
+      setup_groups(pcon->user,pcon->uid,pcon->gid,&pcon->ngroups,&pcon->igroups,&pcon->groups);
+      
+      /* check number of connections */
+      if (!claim_connection(cnum,
+                           lp_servicename(SNUM(cnum)),
+                           lp_max_connections(SNUM(cnum)),False))
+       {
+         DEBUG(1,("too many connections - rejected\n"));
+         return(-8);
+       }  
+
+      if (lp_status(SNUM(cnum)))
+       claim_connection(cnum,"STATUS.",MAXSTATUS,first_connection);
+
+      first_connection = False;
+    } /* IS_IPC */
+
+  pcon->open = True;
+
+  /* execute any "root preexec = " line */
+  if (*lp_rootpreexec(SNUM(cnum)))
+    {
+      pstring cmd;
+      strcpy(cmd,lp_rootpreexec(SNUM(cnum)));
+      standard_sub(cnum,cmd);
+      DEBUG(5,("cmd=%s\n",cmd));
+      smbrun(cmd,NULL);
+    }
+
+  if (!become_user(cnum,pcon->uid))
+    {
+      DEBUG(0,("Can't become connected user!\n"));
+      pcon->open = False;
+      if (!IS_IPC(cnum)) {
+       yield_connection(cnum,
+                        lp_servicename(SNUM(cnum)),
+                        lp_max_connections(SNUM(cnum)));
+       if (lp_status(SNUM(cnum))) yield_connection(cnum,"STATUS.",MAXSTATUS);
+      }
+      return(-1);
+    }
+
+  if (ChDir(pcon->connectpath) != 0)
+    {
+      DEBUG(0,("Can't change directory to %s\n",pcon->connectpath));
+      pcon->open = False;
+      unbecome_user();
+      if (!IS_IPC(cnum)) {
+       yield_connection(cnum,
+                        lp_servicename(SNUM(cnum)),
+                        lp_max_connections(SNUM(cnum)));
+       if (lp_status(SNUM(cnum))) yield_connection(cnum,"STATUS.",MAXSTATUS);
+      }
+      return(-5);      
+    }
+
+  string_set(&pcon->origpath,pcon->connectpath);
+
+#if SOFTLINK_OPTIMISATION
+  /* resolve any soft links early */
+  {
+    pstring s;
+    strcpy(s,pcon->connectpath);
+    GetWd(s);
+    string_set(&pcon->connectpath,s);
+    ChDir(pcon->connectpath);
+  }
+#endif
+
+  num_connections_open++;
+  add_session_user(user);
+  
+  /* execute any "preexec = " line */
+  if (*lp_preexec(SNUM(cnum)))
+    {
+      pstring cmd;
+      strcpy(cmd,lp_preexec(SNUM(cnum)));
+      standard_sub(cnum,cmd);
+      smbrun(cmd,NULL);
+    }
+  
+  /* we've finished with the sensitive stuff */
+  unbecome_user();
+
+  {
+    extern struct from_host Client_info;
+    DEBUG(IS_IPC(cnum)?3:1,("%s %s (%s) connect to service %s as user %s (uid=%d,gid=%d) (pid %d)\n",
+                           timestring(),
+                           Client_info.name,Client_info.addr,
+                           lp_servicename(SNUM(cnum)),user,
+                           pcon->uid,
+                           pcon->gid,
+                           (int)getpid()));
+  }
+
+  return(cnum);
+}
+
+
+/****************************************************************************
+  find first available file slot
+****************************************************************************/
+int find_free_file(void )
+{
+  int i;
+  for (i=1;i<MAX_OPEN_FILES;i++)
+    if (!Files[i].open)
+      return(i);
+  DEBUG(1,("ERROR! Out of file structures - perhaps increase MAX_OPEN_FILES?\n"));
+  return(-1);
+}
+
+/****************************************************************************
+  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.
+****************************************************************************/
+static int find_free_connection(int hash )
+{
+  int i;
+  BOOL used=False;
+  hash = (hash % (MAX_CONNECTIONS-2))+1;
+
+ again:
+
+  for (i=hash+1;i!=hash;)
+    {
+      if (!Connections[i].open && Connections[i].used == used) 
+       {
+         DEBUG(3,("found free connection number %d\n",i));
+         return(i);
+       }
+      i++;
+      if (i == MAX_CONNECTIONS)
+       i = 1;
+    }
+
+  if (!used)
+    {
+      used = !used;
+      goto again;
+    }
+
+  DEBUG(1,("ERROR! Out of connection structures\n"));
+  return(-1);
+}
+
+
+/****************************************************************************
+reply for the core protocol
+****************************************************************************/
+int reply_corep(char *outbuf)
+{
+  int outsize = set_message(outbuf,1,0,True);
+
+  Protocol = PROTOCOL_CORE;
+
+  return outsize;
+}
+
+
+/****************************************************************************
+reply for the coreplus protocol
+****************************************************************************/
+int reply_coreplus(char *outbuf)
+{
+  int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0);
+  int outsize = set_message(outbuf,13,0,True);
+  SSVAL(outbuf,smb_vwv5,raw); /* tell redirector we support
+                                readbraw and writebraw (possibly) */
+  CVAL(outbuf,smb_flg) = 0x81; /* Reply, SMBlockread, SMBwritelock supported */
+  SSVAL(outbuf,smb_vwv1,0x1); /* user level security, don't encrypt */ 
+
+  Protocol = PROTOCOL_COREPLUS;
+
+  return outsize;
+}
+
+
+/****************************************************************************
+reply for the lanman 1.0 protocol
+****************************************************************************/
+int reply_lanman1(char *outbuf)
+{
+  int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0);
+  int secword=0;
+  BOOL doencrypt = SMBENCRYPT();
+  time_t t = time(NULL);
+
+  if (lp_security()>=SEC_USER) secword |= 1;
+  if (doencrypt) secword |= 2;
+
+  set_message(outbuf,13,doencrypt?8:0,True);
+  SSVAL(outbuf,smb_vwv1,secword); 
+#ifdef SMB_PASSWD
+  /* Create a token value and add it to the outgoing packet. */
+  if (doencrypt) 
+    generate_next_challenge(smb_buf(outbuf));
+#endif
+
+  Protocol = PROTOCOL_LANMAN1;
+
+  if (lp_security() == SEC_SERVER && server_cryptkey(outbuf)) {
+    DEBUG(3,("using password server validation\n"));
+#ifdef SMB_PASSWD
+  if (doencrypt) set_challenge(smb_buf(outbuf));    
+#endif
+  }
+
+  CVAL(outbuf,smb_flg) = 0x81; /* Reply, SMBlockread, SMBwritelock supported */
+  SSVAL(outbuf,smb_vwv2,maxxmit);
+  SSVAL(outbuf,smb_vwv3,lp_maxmux()); /* maxmux */
+  SSVAL(outbuf,smb_vwv4,1);
+  SSVAL(outbuf,smb_vwv5,raw); /* tell redirector we support
+                                readbraw writebraw (possibly) */
+  SIVAL(outbuf,smb_vwv6,getpid());
+  SSVAL(outbuf,smb_vwv10, TimeDiff(t)/60);
+
+  put_dos_date(outbuf,smb_vwv8,t);
+
+  return (smb_len(outbuf)+4);
+}
+
+
+/****************************************************************************
+reply for the lanman 2.0 protocol
+****************************************************************************/
+int reply_lanman2(char *outbuf)
+{
+  int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0);
+  int secword=0;
+  BOOL doencrypt = SMBENCRYPT();
+  time_t t = time(NULL);
+
+  if (lp_security()>=SEC_USER) secword |= 1;
+  if (doencrypt) secword |= 2;
+
+  set_message(outbuf,13,doencrypt?8:0,True);
+  SSVAL(outbuf,smb_vwv1,secword); 
+#ifdef SMB_PASSWD
+  /* Create a token value and add it to the outgoing packet. */
+  if (doencrypt) 
+    generate_next_challenge(smb_buf(outbuf));
+#endif
+
+  SIVAL(outbuf,smb_vwv6,getpid());
+
+  Protocol = PROTOCOL_LANMAN2;
+
+  if (lp_security() == SEC_SERVER && server_cryptkey(outbuf)) {
+    DEBUG(3,("using password server validation\n"));
+#ifdef SMB_PASSWD
+    if (doencrypt) set_challenge(smb_buf(outbuf));    
+#endif
+  }
+
+  CVAL(outbuf,smb_flg) = 0x81; /* Reply, SMBlockread, SMBwritelock supported */
+  SSVAL(outbuf,smb_vwv2,maxxmit);
+  SSVAL(outbuf,smb_vwv3,lp_maxmux()); 
+  SSVAL(outbuf,smb_vwv4,1);
+  SSVAL(outbuf,smb_vwv5,raw); /* readbraw and/or writebraw */
+  SSVAL(outbuf,smb_vwv10, TimeDiff(t)/60);
+  put_dos_date(outbuf,smb_vwv8,t);
+
+  return (smb_len(outbuf)+4);
+}
+
+/****************************************************************************
+reply for the nt protocol
+****************************************************************************/
+int reply_nt1(char *outbuf)
+{
+  int capabilities=0x300; /* has dual names + lock_and_read */
+  int secword=0;
+  BOOL doencrypt = SMBENCRYPT();
+
+  if (lp_security()>=SEC_USER) secword |= 1;
+  if (doencrypt) secword |= 2;
+
+  set_message(outbuf,17,doencrypt?8:0,True);
+  CVAL(outbuf,smb_vwv1) = secword;
+#ifdef SMB_PASSWD
+  /* Create a token value and add it to the outgoing packet. */
+  if (doencrypt) {
+    generate_next_challenge(smb_buf(outbuf));
+    /* Tell the nt machine how long the challenge is. */
+    SSVALS(outbuf,smb_vwv16+1,8);
+  }
+#endif
+
+  SIVAL(outbuf,smb_vwv7+1,getpid()); /* session key */
+
+  Protocol = PROTOCOL_NT1;
+
+  if (lp_security() == SEC_SERVER && server_cryptkey(outbuf)) {
+    DEBUG(3,("using password server validation\n"));
+#ifdef SMB_PASSWD
+    if (doencrypt) set_challenge(smb_buf(outbuf));    
+#endif
+  }
+
+  if (lp_readraw() && lp_writeraw())
+    capabilities |= 1;
+
+  SSVAL(outbuf,smb_vwv1+1,lp_maxmux()); /* maxmpx */
+  SSVAL(outbuf,smb_vwv2+1,1); /* num vcs */
+  SIVAL(outbuf,smb_vwv3+1,0xFFFF); /* max buffer */
+  SIVAL(outbuf,smb_vwv5+1,0xFFFF); /* raw size */
+  SIVAL(outbuf,smb_vwv9+1,capabilities); /* capabilities */
+  put_long_date(outbuf+smb_vwv11+1,time(NULL));
+  SSVALS(outbuf,smb_vwv15+1,TimeDiff(time(NULL))/60);
+
+  return (smb_len(outbuf)+4);
+}
+
+
+/* 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]
+
+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  OS/2
+       PC NETWORK PROGRAM 1.0          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      3
+       Windows for Workgroups 3.1a     5       5       5
+       LM1.2X002                                       6      4
+       LANMAN2.1                                       7      5
+       NT LM 0.12                              6       8
+  *
+  *  tim@fsg.com 09/29/95
+  */
+  
+#define ARCH_WFWG     0x3      /* This is a fudge because WfWg is like Win95 */
+#define ARCH_WIN95    0x2
+#define        ARCH_OS2      0xC      /* Again OS/2 is like NT */
+#define ARCH_WINNT    0x8
+#define ARCH_SAMBA    0x10
+#define ARCH_ALL      0x1F
+/* List of supported protocols, most desired first */
+struct {
+  char *proto_name;
+  char *short_name;
+  int (*proto_reply_fn)(char *);
+  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},
+};
+
+
+/****************************************************************************
+  reply to a negprot
+****************************************************************************/
+static int reply_negprot(char *inbuf,char *outbuf)
+{
+  extern fstring remote_arch;
+  int outsize = set_message(outbuf,1,0,True);
+  int Index=0;
+  int choice= -1;
+  int protocol;
+  char *p;
+  int bcc = SVAL(smb_buf(inbuf),-2);
+  int arch = ARCH_ALL;
+
+  p = smb_buf(inbuf)+1;
+  while (p < (smb_buf(inbuf) + bcc))
+    { 
+      Index++;
+      DEBUG(3,("Requested protocol [%s]\n",p));
+      if (strcsequal(p,"Windows for Workgroups 3.1a"))
+       arch &= ( ARCH_WFWG | ARCH_WIN95 | ARCH_WINNT );
+      else if (strcsequal(p,"DOS LM1.2X002"))
+       arch &= ( ARCH_WFWG | ARCH_WIN95 );
+      else if (strcsequal(p,"DOS LANMAN2.1"))
+       arch &= ( ARCH_WFWG | ARCH_WIN95 );
+      else if (strcsequal(p,"NT LM 0.12"))
+       arch &= ( ARCH_WIN95 | ARCH_WINNT );
+      else if (strcsequal(p,"LANMAN2.1"))
+       arch &= ( ARCH_WINNT | ARCH_OS2 );
+      else if (strcsequal(p,"LM1.2X002"))
+       arch &= ( ARCH_WINNT | ARCH_OS2 );
+      else if (strcsequal(p,"MICROSOFT NETWORKS 1.03"))
+       arch &= ARCH_WINNT;
+      else if (strcsequal(p,"XENIX CORE"))
+       arch &= ( ARCH_WINNT | ARCH_OS2 );
+      else if (strcsequal(p,"Samba")) {
+       arch = ARCH_SAMBA;
+       break;
+      }
+      p += strlen(p) + 2;
+    }
+    
+  switch ( arch ) {
+  case ARCH_SAMBA:
+    strcpy(remote_arch,"Samba");
+    break;
+  case ARCH_WFWG:
+    strcpy(remote_arch,"WfWg");
+    break;
+  case ARCH_WIN95:
+    strcpy(remote_arch,"Win95");
+    break;
+  case ARCH_WINNT:
+    strcpy(remote_arch,"WinNT");
+    break;
+  case ARCH_OS2:
+    strcpy(remote_arch,"OS2");
+    break;
+  default:
+    strcpy(remote_arch,"UNKNOWN");
+    break;
+  }
+  /* possibly reload - change of architecture */
+  reload_services(True);      
+    
+  /* a special case to stop password server loops */
+  if (Index == 1 && strequal(remote_machine,myhostname) && 
+      lp_security()==SEC_SERVER)
+    exit_server("Password server loop!");
+  
+  /* Check for protocols, most desirable first */
+  for (protocol = 0; supported_protocols[protocol].proto_name; protocol++)
+    {
+      p = smb_buf(inbuf)+1;
+      Index = 0;
+      if (lp_maxprotocol() >= supported_protocols[protocol].protocol_level)
+       while (p < (smb_buf(inbuf) + bcc))
+         { 
+           if (strequal(p,supported_protocols[protocol].proto_name))
+             choice = Index;
+           Index++;
+           p += strlen(p) + 2;
+         }
+      if(choice != -1)
+       break;
+    }
+  
+  SSVAL(outbuf,smb_vwv0,choice);
+  if(choice != -1) {
+    extern fstring remote_proto;
+    strcpy(remote_proto,supported_protocols[protocol].short_name);
+    reload_services(True);          
+    outsize = supported_protocols[protocol].proto_reply_fn(outbuf);
+    DEBUG(3,("Selected protocol %s\n",supported_protocols[protocol].proto_name));
+  }
+  else {
+    DEBUG(0,("No protocol supported !\n"));
+  }
+  SSVAL(outbuf,smb_vwv0,choice);
+  
+  DEBUG(5,("%s negprot index=%d\n",timestring(),choice));
+
+  return(outsize);
+}
+
+
+/****************************************************************************
+  parse a connect packet
+****************************************************************************/
+void parse_connect(char *buf,char *service,char *user,char *password,int *pwlen,char *dev)
+{
+  char *p = smb_buf(buf) + 1;
+  char *p2;
+
+  DEBUG(4,("parsing connect string %s\n",p));
+    
+  p2 = strrchr(p,'\\');
+  if (p2 == NULL)
+    strcpy(service,p);
+  else
+    strcpy(service,p2+1);
+  
+  p += strlen(p) + 2;
+  
+  strcpy(password,p);
+  *pwlen = strlen(password);
+
+  p += strlen(p) + 2;
+
+  strcpy(dev,p);
+  
+  *user = 0;
+  p = strchr(service,'%');
+  if (p != NULL)
+    {
+      *p = 0;
+      strcpy(user,p+1);
+    }
+}
+
+
+/****************************************************************************
+close all open files for a connection
+****************************************************************************/
+static void close_open_files(int cnum)
+{
+  int i;
+  for (i=0;i<MAX_OPEN_FILES;i++)
+    if( Files[i].cnum == cnum && Files[i].open) {
+      close_file(i);
+    }
+}
+
+
+
+/****************************************************************************
+close a cnum
+****************************************************************************/
+void close_cnum(int cnum, int uid)
+{
+  extern struct from_host Client_info;
+
+  DirCacheFlush(SNUM(cnum));
+
+  unbecome_user();
+
+  if (!OPEN_CNUM(cnum))
+    {
+      DEBUG(0,("Can't close cnum %d\n",cnum));
+      return;
+    }
+
+  DEBUG(IS_IPC(cnum)?3:1,("%s %s (%s) closed connection to service %s\n",
+                         timestring(),
+                         Client_info.name,Client_info.addr,
+                         lp_servicename(SNUM(cnum))));
+
+  yield_connection(cnum,
+                  lp_servicename(SNUM(cnum)),
+                  lp_max_connections(SNUM(cnum)));
+
+  if (lp_status(SNUM(cnum)))
+    yield_connection(cnum,"STATUS.",MAXSTATUS);
+
+  close_open_files(cnum);
+  dptr_closecnum(cnum);
+
+  /* execute any "postexec = " line */
+  if (*lp_postexec(SNUM(cnum)) && become_user(cnum,uid))
+    {
+      pstring cmd;
+      strcpy(cmd,lp_postexec(SNUM(cnum)));
+      standard_sub(cnum,cmd);
+      smbrun(cmd,NULL);
+      unbecome_user();
+    }
+
+  unbecome_user();
+  /* execute any "root postexec = " line */
+  if (*lp_rootpostexec(SNUM(cnum)))
+    {
+      pstring cmd;
+      strcpy(cmd,lp_rootpostexec(SNUM(cnum)));
+      standard_sub(cnum,cmd);
+      smbrun(cmd,NULL);
+    }
+
+  Connections[cnum].open = False;
+  num_connections_open--;
+  if (Connections[cnum].ngroups && Connections[cnum].groups)
+    {
+      if (Connections[cnum].igroups != (int *)Connections[cnum].groups)
+       free(Connections[cnum].groups);
+      free(Connections[cnum].igroups);
+      Connections[cnum].groups = NULL;
+      Connections[cnum].igroups = NULL;
+      Connections[cnum].ngroups = 0;
+    }
+
+  string_set(&Connections[cnum].user,"");
+  string_set(&Connections[cnum].dirpath,"");
+  string_set(&Connections[cnum].connectpath,"");
+}
+
+
+/****************************************************************************
+simple routines to do connection counting
+****************************************************************************/
+BOOL yield_connection(int cnum,char *name,int max_connections)
+{
+  struct connect_record crec;
+  pstring fname;
+  FILE *f;
+  int mypid = getpid();
+  int i;
+
+  DEBUG(3,("Yielding connection to %d %s\n",cnum,name));
+
+  if (max_connections <= 0)
+    return(True);
+
+  bzero(&crec,sizeof(crec));
+
+  strcpy(fname,lp_lockdir());
+  standard_sub(cnum,fname);
+  trim_string(fname,"","/");
+
+  strcat(fname,"/");
+  strcat(fname,name);
+  strcat(fname,".LCK");
+
+  f = fopen(fname,"r+");
+  if (!f)
+    {
+      DEBUG(2,("Coudn't open lock file %s (%s)\n",fname,strerror(errno)));
+      return(False);
+    }
+
+  fseek(f,0,SEEK_SET);
+
+  /* find a free spot */
+  for (i=0;i<max_connections;i++)
+    {
+      if (fread(&crec,sizeof(crec),1,f) != 1)
+       {
+         DEBUG(2,("Entry not found in lock file %s\n",fname));
+         fclose(f);
+         return(False);
+       }
+      if (crec.pid == mypid && crec.cnum == cnum)
+       break;
+    }
+
+  if (crec.pid != mypid || crec.cnum != cnum)
+    {
+      fclose(f);
+      DEBUG(2,("Entry not found in lock file %s\n",fname));
+      return(False);
+    }
+
+  bzero((void *)&crec,sizeof(crec));
+  
+  /* remove our mark */
+  if (fseek(f,i*sizeof(crec),SEEK_SET) != 0 ||
+      fwrite(&crec,sizeof(crec),1,f) != 1)
+    {
+      DEBUG(2,("Couldn't update lock file %s (%s)\n",fname,strerror(errno)));
+      fclose(f);
+      return(False);
+    }
+
+  DEBUG(3,("Yield successful\n"));
+
+  fclose(f);
+  return(True);
+}
+
+
+/****************************************************************************
+simple routines to do connection counting
+****************************************************************************/
+BOOL claim_connection(int cnum,char *name,int max_connections,BOOL Clear)
+{
+  struct connect_record crec;
+  pstring fname;
+  FILE *f;
+  int snum = SNUM(cnum);
+  int i,foundi= -1;
+  int total_recs;
+
+  if (max_connections <= 0)
+    return(True);
+
+  DEBUG(5,("trying claim %s %s %d\n",lp_lockdir(),name,max_connections));
+
+  strcpy(fname,lp_lockdir());
+  standard_sub(cnum,fname);
+  trim_string(fname,"","/");
+
+  if (!directory_exist(fname,NULL))
+    mkdir(fname,0755);
+
+  strcat(fname,"/");
+  strcat(fname,name);
+  strcat(fname,".LCK");
+
+  if (!file_exist(fname,NULL))
+    {
+      f = fopen(fname,"w");
+      if (f) fclose(f);
+    }
+
+  total_recs = file_size(fname) / sizeof(crec);
+
+  f = fopen(fname,"r+");
+
+  if (!f)
+    {
+      DEBUG(1,("couldn't open lock file %s\n",fname));
+      return(False);
+    }
+
+  /* find a free spot */
+  for (i=0;i<max_connections;i++)
+    {
+
+      if (i>=total_recs || 
+         fseek(f,i*sizeof(crec),SEEK_SET) != 0 ||
+         fread(&crec,sizeof(crec),1,f) != 1)
+       {
+         if (foundi < 0) foundi = i;
+         break;
+       }
+
+      if (Clear && crec.pid && !process_exists(crec.pid))
+       {
+         fseek(f,i*sizeof(crec),SEEK_SET);
+         bzero((void *)&crec,sizeof(crec));
+         fwrite(&crec,sizeof(crec),1,f);
+         if (foundi < 0) foundi = i;
+         continue;
+       }
+      if (foundi < 0 && (!crec.pid || !process_exists(crec.pid)))
+       {
+         foundi=i;
+         if (!Clear) break;
+       }
+    }  
+
+  if (foundi < 0)
+    {
+      DEBUG(3,("no free locks in %s\n",fname));
+      fclose(f);
+      return(False);
+    }      
+
+  /* fill in the crec */
+  bzero((void *)&crec,sizeof(crec));
+  crec.magic = 0x280267;
+  crec.pid = getpid();
+  crec.cnum = cnum;
+  crec.uid = Connections[cnum].uid;
+  crec.gid = Connections[cnum].gid;
+  StrnCpy(crec.name,lp_servicename(snum),sizeof(crec.name)-1);
+  crec.start = time(NULL);
+
+  {
+    extern struct from_host Client_info;
+    StrnCpy(crec.machine,Client_info.name,sizeof(crec.machine)-1);
+    StrnCpy(crec.addr,Client_info.addr,sizeof(crec.addr)-1);
+  }
+  
+  /* make our mark */
+  if (fseek(f,foundi*sizeof(crec),SEEK_SET) != 0 ||
+      fwrite(&crec,sizeof(crec),1,f) != 1)
+    {
+      fclose(f);
+      return(False);
+    }
+
+  fclose(f);
+  return(True);
+}
+
+#if DUMP_CORE
+/*******************************************************************
+prepare to dump a core file - carefully!
+********************************************************************/
+static BOOL dump_core(void)
+{
+  char *p;
+  pstring dname;
+  strcpy(dname,debugf);
+  if ((p=strrchr(dname,'/'))) *p=0;
+  strcat(dname,"/corefiles");
+  mkdir(dname,0700);
+  sys_chown(dname,getuid(),getgid());
+  chmod(dname,0700);
+  if (chdir(dname)) return(False);
+  umask(~(0700));
+
+#ifndef NO_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",rlp.rlim_cur,rlp.rlim_max));
+  }
+#endif
+#endif
+
+
+  DEBUG(0,("Dumping core in %s\n",dname));
+  return(True);
+}
+#endif
+
+/****************************************************************************
+exit the server
+****************************************************************************/
+void exit_server(char *reason)
+{
+  static int firsttime=1;
+  int i;
+
+  if (!firsttime) exit(0);
+  firsttime = 0;
+
+  unbecome_user();
+  DEBUG(2,("Closing connections\n"));
+  for (i=0;i<MAX_CONNECTIONS;i++)
+    if (Connections[i].open)
+      close_cnum(i,-1);
+#ifdef DFS_AUTH
+  if (dcelogin_atmost_once)
+    dfs_unlogin();
+#endif
+  if (!reason) {   
+    int oldlevel = DEBUGLEVEL;
+    DEBUGLEVEL = 10;
+    DEBUG(0,("Last message was %s\n",smb_fn_name(last_message)));
+    if (last_inbuf)
+      show_msg(last_inbuf);
+    DEBUGLEVEL = oldlevel;
+    DEBUG(0,("===============================================================\n"));
+#if DUMP_CORE
+    if (dump_core()) return;
+#endif
+  }    
+  DEBUG(3,("%s Server exit  (%s)\n",timestring(),reason?reason:""));
+  exit(0);
+}
+
+/****************************************************************************
+do some standard substitutions in a string
+****************************************************************************/
+void standard_sub(int cnum,char *s)
+{
+  if (!strchr(s,'%')) return;
+
+  if (VALID_CNUM(cnum))
+    {
+      string_sub(s,"%S",lp_servicename(Connections[cnum].service));
+      string_sub(s,"%P",Connections[cnum].connectpath);
+      string_sub(s,"%u",Connections[cnum].user);
+      if (strstr(s,"%H")) {
+       char *home = get_home_dir(Connections[cnum].user);
+       if (home) string_sub(s,"%H",home);
+      }
+      string_sub(s,"%g",gidtoname(Connections[cnum].gid));
+    }
+  standard_sub_basic(s);
+}
+
+/*
+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 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!
+*/
+struct smb_message_struct
+{
+  int code;
+  char *name;
+  int (*fn)();
+  int flags;
+#if PROFILING
+  unsigned long time;
+#endif
+}
+ smb_messages[] = {
+
+    /* CORE PROTOCOL */
+
+   {SMBnegprot,"SMBnegprot",reply_negprot,0},
+   {SMBtcon,"SMBtcon",reply_tcon,0},
+   {SMBtdis,"SMBtdis",reply_tdis,0},
+   {SMBexit,"SMBexit",reply_exit,0},
+   {SMBioctl,"SMBioctl",reply_ioctl,0},
+   {SMBecho,"SMBecho",reply_echo,0},
+   {SMBsesssetupX,"SMBsesssetupX",reply_sesssetup_and_X,0},
+   {SMBtconX,"SMBtconX",reply_tcon_and_X,0},
+   {SMBulogoffX, "SMBulogoffX", reply_ulogoffX, 0}, 
+   {SMBgetatr,"SMBgetatr",reply_getatr,AS_USER},
+   {SMBsetatr,"SMBsetatr",reply_setatr,AS_USER | NEED_WRITE},
+   {SMBchkpth,"SMBchkpth",reply_chkpth,AS_USER},
+   {SMBsearch,"SMBsearch",reply_search,AS_USER},
+   {SMBopen,"SMBopen",reply_open,AS_USER},
+
+   /* note that SMBmknew and SMBcreate are deliberately overloaded */   
+   {SMBcreate,"SMBcreate",reply_mknew,AS_USER},
+   {SMBmknew,"SMBmknew",reply_mknew,AS_USER}, 
+
+   {SMBunlink,"SMBunlink",reply_unlink,AS_USER | NEED_WRITE},
+   {SMBread,"SMBread",reply_read,AS_USER},
+   {SMBwrite,"SMBwrite",reply_write,AS_USER},
+   {SMBclose,"SMBclose",reply_close,AS_USER},
+   {SMBmkdir,"SMBmkdir",reply_mkdir,AS_USER | NEED_WRITE},
+   {SMBrmdir,"SMBrmdir",reply_rmdir,AS_USER | NEED_WRITE},
+   {SMBdskattr,"SMBdskattr",reply_dskattr,AS_USER},
+   {SMBmv,"SMBmv",reply_mv,AS_USER | NEED_WRITE},
+
+   /* this is a Pathworks specific call, allowing the 
+      changing of the root path */
+   {pSETDIR,"pSETDIR",reply_setdir,AS_USER}, 
+
+   {SMBlseek,"SMBlseek",reply_lseek,AS_USER},
+   {SMBflush,"SMBflush",reply_flush,AS_USER},
+   {SMBctemp,"SMBctemp",reply_ctemp,AS_USER},
+   {SMBsplopen,"SMBsplopen",reply_printopen,AS_USER},
+   {SMBsplclose,"SMBsplclose",reply_printclose,AS_USER},
+   {SMBsplretq,"SMBsplretq",reply_printqueue,AS_USER},
+   {SMBsplwr,"SMBsplwr",reply_printwrite,AS_USER},
+   {SMBlock,"SMBlock",reply_lock,AS_USER},
+   {SMBunlock,"SMBunlock",reply_unlock,AS_USER},
+   
+   /* CORE+ PROTOCOL FOLLOWS */
+   
+   {SMBreadbraw,"SMBreadbraw",reply_readbraw,AS_USER},
+   {SMBwritebraw,"SMBwritebraw",reply_writebraw,AS_USER},
+   {SMBwriteclose,"SMBwriteclose",reply_writeclose,AS_USER},
+   {SMBlockread,"SMBlockread",reply_lockread,AS_USER},
+   {SMBwriteunlock,"SMBwriteunlock",reply_writeunlock,AS_USER},
+   
+   /* LANMAN1.0 PROTOCOL FOLLOWS */
+   
+   {SMBreadBmpx,"SMBreadBmpx",reply_readbmpx,AS_USER},
+   {SMBreadBs,"SMBreadBs",NULL,AS_USER},
+   {SMBwriteBmpx,"SMBwriteBmpx",reply_writebmpx,AS_USER},
+   {SMBwriteBs,"SMBwriteBs",reply_writebs,AS_USER},
+   {SMBwritec,"SMBwritec",NULL,AS_USER},
+   {SMBsetattrE,"SMBsetattrE",reply_setattrE,AS_USER | NEED_WRITE},
+   {SMBgetattrE,"SMBgetattrE",reply_getattrE,AS_USER},
+   {SMBtrans,"SMBtrans",reply_trans,AS_USER | CAN_IPC},
+   {SMBtranss,"SMBtranss",NULL,AS_USER | CAN_IPC},
+   {SMBioctls,"SMBioctls",NULL,AS_USER},
+   {SMBcopy,"SMBcopy",reply_copy,AS_USER | NEED_WRITE},
+   {SMBmove,"SMBmove",NULL,AS_USER | NEED_WRITE},
+   
+   {SMBopenX,"SMBopenX",reply_open_and_X,AS_USER},
+   {SMBreadX,"SMBreadX",reply_read_and_X,AS_USER},
+   {SMBwriteX,"SMBwriteX",reply_write_and_X,AS_USER},
+   {SMBlockingX,"SMBlockingX",reply_lockingX,AS_USER},
+   
+   {SMBffirst,"SMBffirst",reply_search,AS_USER},
+   {SMBfunique,"SMBfunique",reply_search,AS_USER},
+   {SMBfclose,"SMBfclose",reply_fclose,AS_USER},
+
+   /* LANMAN2.0 PROTOCOL FOLLOWS */
+   {SMBfindnclose, "SMBfindnclose", reply_findnclose, AS_USER},
+   {SMBfindclose, "SMBfindclose", reply_findclose,AS_USER},
+   {SMBtrans2, "SMBtrans2", reply_trans2, AS_USER},
+   {SMBtranss2, "SMBtranss2", reply_transs2, AS_USER},
+
+   /* messaging routines */
+   {SMBsends,"SMBsends",reply_sends,AS_GUEST},
+   {SMBsendstrt,"SMBsendstrt",reply_sendstrt,AS_GUEST},
+   {SMBsendend,"SMBsendend",reply_sendend,AS_GUEST},
+   {SMBsendtxt,"SMBsendtxt",reply_sendtxt,AS_GUEST},
+
+   /* NON-IMPLEMENTED PARTS OF THE CORE PROTOCOL */
+   
+   {SMBsendb,"SMBsendb",NULL,AS_GUEST},
+   {SMBfwdname,"SMBfwdname",NULL,AS_GUEST},
+   {SMBcancelf,"SMBcancelf",NULL,AS_GUEST},
+   {SMBgetmac,"SMBgetmac",NULL,AS_GUEST}
+ };
+
+/****************************************************************************
+return a string containing the function name of a SMB command
+****************************************************************************/
+char *smb_fn_name(int type)
+{
+  static char *unknown_name = "SMBunknown";
+  static int num_smb_messages = 
+    sizeof(smb_messages) / sizeof(struct smb_message_struct);
+  int match;
+
+  for (match=0;match<num_smb_messages;match++)
+    if (smb_messages[match].code == type)
+      break;
+
+  if (match == num_smb_messages)
+    return(unknown_name);
+
+  return(smb_messages[match].name);
+}
+
+
+/****************************************************************************
+do a switch on the message type, and return the response size
+****************************************************************************/
+static int switch_message(int type,char *inbuf,char *outbuf,int size,int bufsize)
+{
+  static int pid= -1;
+  int outsize = 0;
+  static int num_smb_messages = 
+    sizeof(smb_messages) / sizeof(struct smb_message_struct);
+  int match;
+
+#if PROFILING
+  struct timeval msg_start_time;
+  struct timeval msg_end_time;
+  static unsigned long total_time = 0;
+
+  GetTimeOfDay(&msg_start_time);
+#endif
+
+  if (pid == -1)
+    pid = getpid();
+
+  errno = 0;
+  last_message = type;
+
+  /* make sure this is an SMB packet */
+  if (strncmp(smb_base(inbuf),"\377SMB",4) != 0)
+    {
+      DEBUG(2,("Non-SMB packet of length %d\n",smb_len(inbuf)));
+      return(-1);
+    }
+
+  for (match=0;match<num_smb_messages;match++)
+    if (smb_messages[match].code == type)
+      break;
+
+  if (match == num_smb_messages)
+    {
+      DEBUG(0,("Unknown message type %d!\n",type));
+      outsize = reply_unknown(inbuf,outbuf);
+    }
+  else
+    {
+      DEBUG(3,("switch message %s (pid %d)\n",smb_messages[match].name,pid));
+      if (smb_messages[match].fn)
+       {
+         int cnum = SVAL(inbuf,smb_tid);
+         int flags = smb_messages[match].flags;
+         int uid = SVAL(inbuf,smb_uid);
+
+         /* does this protocol need to be run as root? */
+         if (!(flags & AS_USER))
+           unbecome_user();
+
+         /* does this protocol need to be run as the connected user? */
+         if ((flags & AS_USER) && !become_user(cnum,uid))
+           return(ERROR(ERRSRV,ERRinvnid));
+
+         /* does it need write permission? */
+         if ((flags & NEED_WRITE) && !CAN_WRITE(cnum))
+           return(ERROR(ERRSRV,ERRaccess));
+
+         /* ipc services are limited */
+         if (IS_IPC(cnum) && (flags & AS_USER) && !(flags & CAN_IPC))
+           return(ERROR(ERRSRV,ERRaccess));        
+
+         /* load service specific parameters */
+         if (OPEN_CNUM(cnum) && !become_service(cnum,(flags & AS_USER)?True:False))
+           return(ERROR(ERRSRV,ERRaccess));
+
+         /* does this protocol need to be run as guest? */
+         if ((flags & AS_GUEST) && (!become_guest() || !check_access(-1)))
+           return(ERROR(ERRSRV,ERRaccess));
+
+         last_inbuf = inbuf;
+
+         outsize = smb_messages[match].fn(inbuf,outbuf,size,bufsize);
+       }
+      else
+       {
+         outsize = reply_unknown(inbuf,outbuf);
+       }
+    }
+
+#if PROFILING
+  GetTimeOfDay(&msg_end_time);
+  if (!(smb_messages[match].flags & TIME_INIT))
+    {
+      smb_messages[match].time = 0;
+      smb_messages[match].flags |= TIME_INIT;
+    }
+  {
+    unsigned long this_time =     
+      (msg_end_time.tv_sec - msg_start_time.tv_sec)*1e6 +
+       (msg_end_time.tv_usec - msg_start_time.tv_usec);
+    smb_messages[match].time += this_time;
+    total_time += this_time;
+  }
+  DEBUG(2,("TIME %s  %d usecs   %g pct\n",
+          smb_fn_name(type),smb_messages[match].time,
+       (100.0*smb_messages[match].time) / total_time));
+#endif
+
+  return(outsize);
+}
+
+
+/****************************************************************************
+construct a chained reply and add it to the already made reply
+
+inbuf points to the original message start.
+inbuf2 points to the smb_wct part of the secondary message
+type is the type of the secondary message
+outbuf points to the original outbuffer
+outbuf2 points to the smb_wct field of the new outbuffer
+size is the total length of the incoming message (from inbuf1)
+bufsize is the total buffer size
+
+return how many bytes were added to the response
+****************************************************************************/
+int chain_reply(int type,char *inbuf,char *inbuf2,char *outbuf,char *outbuf2,int size,int bufsize)
+{
+  int outsize = 0;
+  char *ibuf,*obuf;
+  static BOOL in_chain = False;
+  static char *last_outbuf=NULL;
+  BOOL was_inchain = in_chain;
+  int insize_remaining;
+  static int insize_deleted;
+
+  chain_size += PTR_DIFF(outbuf2,outbuf) - smb_wct;
+  if (was_inchain)
+    outbuf = last_outbuf;
+  else
+    insize_deleted = 0;
+
+
+  insize_deleted = 0;
+  inbuf2 -= insize_deleted;
+  insize_remaining = size - PTR_DIFF(inbuf2,inbuf);
+  insize_deleted += size - (insize_remaining + smb_wct);
+
+  in_chain = True;
+  last_outbuf = outbuf;
+
+
+  /* allocate some space for the in and out buffers of the chained message */
+  ibuf = (char *)malloc(size + SAFETY_MARGIN);
+  obuf = (char *)malloc(bufsize + SAFETY_MARGIN);
+
+  if (!ibuf || !obuf)
+    {
+      DEBUG(0,("Out of memory in chain reply\n"));
+      return(ERROR(ERRSRV,ERRnoresource));
+    }
+
+  ibuf += SMB_ALIGNMENT;
+  obuf += SMB_ALIGNMENT;
+
+  /* create the in buffer */
+  memcpy(ibuf,inbuf,smb_wct);
+  memcpy(ibuf+smb_wct,inbuf2,insize_remaining);
+  CVAL(ibuf,smb_com) = type;
+
+  /* create the out buffer */
+  bzero(obuf,smb_size);
+
+  set_message(obuf,0,0,True);
+  CVAL(obuf,smb_com) = CVAL(ibuf,smb_com);
+  
+  memcpy(obuf+4,ibuf+4,4);
+  CVAL(obuf,smb_rcls) = SUCCESS;
+  CVAL(obuf,smb_reh) = 0;
+  CVAL(obuf,smb_flg) = 0x80 | (CVAL(ibuf,smb_flg) & 0x8); /* bit 7 set 
+                                                            means a reply */
+  SSVAL(obuf,smb_flg2,1); /* say we support long filenames */
+  SSVAL(obuf,smb_err,SUCCESS);
+  SSVAL(obuf,smb_tid,SVAL(inbuf,smb_tid));
+  SSVAL(obuf,smb_pid,SVAL(inbuf,smb_pid));
+  SSVAL(obuf,smb_uid,SVAL(inbuf,smb_uid));
+  SSVAL(obuf,smb_mid,SVAL(inbuf,smb_mid));
+
+  DEBUG(3,("Chained message\n"));
+  show_msg(ibuf);
+
+  /* process the request */
+  outsize = switch_message(type,ibuf,obuf,smb_wct+insize_remaining,
+                          bufsize-chain_size);
+
+  /* copy the new reply header over the old one, but preserve 
+     the smb_com field */
+  memcpy(outbuf+smb_com+1,obuf+smb_com+1,smb_wct-(smb_com+1));
+
+  /* and copy the data from the reply to the right spot */
+  memcpy(outbuf2,obuf+smb_wct,outsize - smb_wct);
+
+  /* free the allocated buffers */
+  if (ibuf) free(ibuf-SMB_ALIGNMENT);
+  if (obuf) free(obuf-SMB_ALIGNMENT);
+
+  in_chain = was_inchain;
+
+  /* return how much extra has been added to the packet */
+  return(outsize - smb_wct);
+}
+
+
+
+/****************************************************************************
+  construct a reply to the incoming packet
+****************************************************************************/
+int construct_reply(char *inbuf,char *outbuf,int size,int bufsize)
+{
+  int type = CVAL(inbuf,smb_com);
+  int outsize = 0;
+  int msg_type = CVAL(inbuf,0);
+
+  smb_last_time = time(NULL);
+
+  chain_size = 0;
+
+  bzero(outbuf,smb_size);
+
+  if (msg_type != 0)
+    return(reply_special(inbuf,outbuf));  
+
+  CVAL(outbuf,smb_com) = CVAL(inbuf,smb_com);
+  set_message(outbuf,0,0,True);
+  
+  memcpy(outbuf+4,inbuf+4,4);
+  CVAL(outbuf,smb_rcls) = SUCCESS;
+  CVAL(outbuf,smb_reh) = 0;
+  CVAL(outbuf,smb_flg) = 0x80 | (CVAL(inbuf,smb_flg) & 0x8); /* bit 7 set 
+                                                            means a reply */
+  SSVAL(outbuf,smb_flg2,1); /* say we support long filenames */
+  SSVAL(outbuf,smb_err,SUCCESS);
+  SSVAL(outbuf,smb_tid,SVAL(inbuf,smb_tid));
+  SSVAL(outbuf,smb_pid,SVAL(inbuf,smb_pid));
+  SSVAL(outbuf,smb_uid,SVAL(inbuf,smb_uid));
+  SSVAL(outbuf,smb_mid,SVAL(inbuf,smb_mid));
+
+  outsize = switch_message(type,inbuf,outbuf,size,bufsize);
+
+  if(outsize > 4)
+    smb_setlen(outbuf,outsize - 4);
+  return(outsize);
+}
+
+
+/****************************************************************************
+  process commands from the client
+****************************************************************************/
+void process(void )
+{
+  static int trans_num = 0;
+  int nread;
+  extern struct from_host Client_info;
+  extern int Client;
+
+  fromhost(Client,&Client_info);
+  
+  InBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+  OutBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+  if ((InBuffer == NULL) || (OutBuffer == NULL)) 
+    return;
+
+  InBuffer += SMB_ALIGNMENT;
+  OutBuffer += SMB_ALIGNMENT;
+
+#if PRIME_NMBD
+  DEBUG(3,("priming nmbd\n"));
+  {
+    struct in_addr ip;
+    ip = *interpret_addr2("localhost");
+    if (zero_ip(ip)) ip = *interpret_addr2("127.0.0.1");
+    *OutBuffer = 0;
+    send_one_packet(OutBuffer,1,ip,137,SOCK_DGRAM);
+  }
+#endif    
+
+  last_user.cnum = -1;
+  
+  while (True)
+    {
+      int32 len;      
+      int msg_type;
+      int msg_flags;
+      int type;
+      int deadtime = lp_deadtime()*60;
+      int counter;
+      int last_keepalive=0;
+
+      if (deadtime <= 0)
+       deadtime = DEFAULT_SMBD_TIMEOUT;
+
+      if (lp_readprediction())
+       do_read_prediction();
+
+      {
+       extern pstring share_del_pending;
+       if (*share_del_pending) {
+         unbecome_user();
+         if (!unlink(share_del_pending))
+           DEBUG(3,("Share file deleted %s\n",share_del_pending));
+         else
+           DEBUG(2,("Share del failed of %s\n",share_del_pending));
+         share_del_pending[0] = 0;
+       }
+      }
+
+      if (share_mode_pending) {
+       unbecome_user();
+       check_share_modes();
+       share_mode_pending=False;
+      }
+
+      errno = 0;      
+
+      for (counter=SMBD_SELECT_LOOP; 
+          !receive_smb(Client,InBuffer,SMBD_SELECT_LOOP*1000); 
+          counter += SMBD_SELECT_LOOP)
+       {
+         int i;
+         time_t t;
+         BOOL allidle = True;
+         extern int keepalive;
+
+         /* check for socket failure */
+         if (errno == EBADF) {
+           DEBUG(3,("%s Bad file descriptor - exiting\n",timestring()));
+           return;
+         }
+
+         t = time(NULL);
+
+         /* become root again if waiting */
+         unbecome_user();
+
+         /* check for smb.conf reload */
+         if (!(counter%SMBD_RELOAD_CHECK))
+           reload_services(True);
+
+         /* check the share modes every 10 secs */
+         if (!(counter%SHARE_MODES_CHECK))
+           check_share_modes();
+
+         /* clean the share modes every 5 minutes */
+         if (!(counter%SHARE_MODES_CLEAN))
+           clean_share_files();
+
+         /* automatic timeout if all connections are closed */      
+         if (num_connections_open==0 && counter >= IDLE_CLOSED_TIMEOUT) {
+           DEBUG(2,("%s Closing idle connection\n",timestring()));
+           return;
+         }
+
+         if (keepalive && (counter-last_keepalive)>keepalive) {
+           if (!send_keepalive(Client)) {
+             DEBUG(2,("%s Keepalive failed - exiting\n",timestring()));
+             return;
+           }
+           last_keepalive = counter;
+         }
+
+         /* check for connection timeouts */
+         for (i=0;i<MAX_CONNECTIONS;i++)
+           if (Connections[i].open)
+             {
+               /* close dirptrs on connections that are idle */
+               if ((t-Connections[i].lastused)>DPTR_IDLE_TIMEOUT)
+                 dptr_idlecnum(i);
+
+               if (Connections[i].num_files_open > 0 ||
+                   (t-Connections[i].lastused)<deadtime)
+                 allidle = False;
+             }
+
+         if (allidle && num_connections_open>0) {
+           DEBUG(2,("%s Closing idle connection 2\n",timestring()));
+           return;
+         }
+       }
+
+      msg_type = CVAL(InBuffer,0);
+      msg_flags = CVAL(InBuffer,1);
+      type = CVAL(InBuffer,smb_com);
+
+      len = smb_len(InBuffer);
+
+      DEBUG(6,("got message type 0x%x of len 0x%x\n",msg_type,len));
+
+      nread = len + 4;
+      
+      DEBUG(3,("%s Transaction %d of length %d\n",timestring(),trans_num,nread));
+
+#ifdef WITH_VTP
+      if(trans_num == 1 && VT_Check(InBuffer)) {
+        VT_Process();
+        return;
+      }
+#endif
+
+
+      if (msg_type == 0)
+       show_msg(InBuffer);
+
+      nread = construct_reply(InBuffer,OutBuffer,nread,maxxmit);
+      
+      if(nread > 0) {
+        if (CVAL(OutBuffer,0) == 0)
+         show_msg(OutBuffer);
+       
+        if (nread != smb_len(OutBuffer) + 4) 
+         {
+           DEBUG(0,("ERROR: Invalid message response size! %d %d\n",
+                    nread,
+                    smb_len(OutBuffer)));
+         }
+       else
+         send_smb(Client,OutBuffer);
+      }
+      trans_num++;
+    }
+}
+
+
+/****************************************************************************
+  initialise connect, service and file structs
+****************************************************************************/
+static void init_structs(void )
+{
+  int i;
+  get_myname(myhostname,&myip);
+
+  for (i=0;i<MAX_CONNECTIONS;i++)
+    {
+      Connections[i].open = False;
+      Connections[i].num_files_open=0;
+      Connections[i].lastused=0;
+      Connections[i].used=False;
+      string_init(&Connections[i].user,"");
+      string_init(&Connections[i].dirpath,"");
+      string_init(&Connections[i].connectpath,"");
+      string_init(&Connections[i].origpath,"");
+    }
+
+  for (i=0;i<MAX_OPEN_FILES;i++)
+    {
+      Files[i].open = False;
+      string_init(&Files[i].name,"");
+    }
+
+  init_dptrs();
+}
+
+/****************************************************************************
+usage on the program
+****************************************************************************/
+void usage(char *pname)
+{
+  DEBUG(0,("Incorrect program usage - are you sure the command line is correct?\n"));
+
+  printf("Usage: %s [-D] [-p port] [-d debuglevel] [-l log basename] [-s services file]\n",pname);
+  printf("Version %s\n",VERSION);
+  printf("\t-D                    become a daemon\n");
+  printf("\t-p port               listen on the specified port\n");
+  printf("\t-d debuglevel         set the debuglevel\n");
+  printf("\t-l log basename.      Basename for log/debug files\n");
+  printf("\t-s services file.     Filename of services file\n");
+  printf("\t-P                    passive only\n");
+  printf("\t-a                    overwrite log file, don't append\n");
+  printf("\n");
+}
+
+
+/****************************************************************************
+  main program
+****************************************************************************/
+int main(int argc,char *argv[])
+{
+  extern BOOL append_log;
+  /* shall I run as a daemon */
+  BOOL is_daemon = False;
+  int port = 139;
+  int opt;
+  extern char *optarg;
+
+#ifdef NEED_AUTH_PARAMETERS
+  set_auth_parameters(argc,argv);
+#endif
+
+#ifdef SecureWare
+  setluid(0);
+#endif
+
+  append_log = True;
+
+  TimeInit();
+
+  strcpy(debugf,SMBLOGFILE);  
+
+  setup_logging(argv[0],False);
+
+  charset_initialise();
+
+  /* make absolutely sure we run as root - to handle cases whre people
+     are crazy enough to have it setuid */
+#ifdef USE_SETRES
+  setresuid(0,0,0);
+#else
+  setuid(0);
+  seteuid(0);
+  setuid(0);
+  seteuid(0);
+#endif
+
+  fault_setup(exit_server);
+
+  umask(0777 & ~DEF_CREATE_MASK);
+
+  initial_uid = geteuid();
+  initial_gid = getegid();
+
+  if (initial_gid != 0 && initial_uid == 0)
+    {
+#ifdef HPUX
+      setresgid(0,0,0);
+#else
+      setgid(0);
+      setegid(0);
+#endif
+    }
+
+  initial_uid = geteuid();
+  initial_gid = getegid();
+
+
+  /* this is for people who can't start the program correctly */
+  while (argc > 1 && (*argv[1] != '-'))
+    {
+      argv++;
+      argc--;
+    }
+
+  while ((opt = getopt(argc, argv, "O:i:l:s:d:Dp:hPa")) != EOF)
+    switch (opt)
+      {
+      case 'O':
+       strcpy(user_socket_options,optarg);
+       break;
+      case 'i':
+       strcpy(scope,optarg);
+       break;
+      case 'P':
+       {
+         extern BOOL passive;
+         passive = True;
+       }
+       break;  
+      case 's':
+       strcpy(servicesf,optarg);
+       break;
+      case 'l':
+       strcpy(debugf,optarg);
+       break;
+      case 'a':
+       {
+         extern BOOL append_log;
+         append_log = !append_log;
+       }
+       break;
+      case 'D':
+       is_daemon = True;
+       break;
+      case 'd':
+       if (*optarg == 'A')
+         DEBUGLEVEL = 10000;
+       else
+         DEBUGLEVEL = atoi(optarg);
+       break;
+      case 'p':
+       port = atoi(optarg);
+       break;
+      case 'h':
+       usage(argv[0]);
+       exit(0);
+       break;
+      default:
+       usage(argv[0]);
+       exit(1);
+      }
+
+  reopen_logs();
+
+  DEBUG(2,("%s smbd version %s started\n",timestring(),VERSION));
+  DEBUG(2,("Copyright Andrew Tridgell 1992-1995\n"));
+
+  GetWd(OriginalDir);
+
+#ifndef NO_GETRLIMIT
+#ifdef RLIMIT_NOFILE
+  {
+    struct rlimit rlp;
+    getrlimit(RLIMIT_NOFILE, &rlp);
+    rlp.rlim_cur = (MAX_OPEN_FILES>rlp.rlim_max)? rlp.rlim_max:MAX_OPEN_FILES;
+    setrlimit(RLIMIT_NOFILE, &rlp);
+    getrlimit(RLIMIT_NOFILE, &rlp);
+    DEBUG(3,("Maximum number of open files per session is %d\n",rlp.rlim_cur));
+  }
+#endif
+#endif
+
+  
+  DEBUG(2,("uid=%d gid=%d euid=%d egid=%d\n",
+       getuid(),getgid(),geteuid(),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);
+    }
+
+  init_structs();
+
+  if (!reload_services(False))
+    return(-1);        
+
+#ifndef NO_SIGNAL_TEST
+  signal(SIGHUP,SIGNAL_CAST sig_hup);
+#endif
+  
+  DEBUG(3,("%s loaded services\n",timestring()));
+
+  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)
+    {
+      DEBUG(3,("%s becoming a daemon\n",timestring()));
+      become_daemon();
+    }
+
+  if (open_sockets(is_daemon,port))
+    {      
+      /* possibly reload the services file. */
+      reload_services(True);
+
+      maxxmit = MIN(lp_maxxmit(),BUFFER_SIZE);
+
+      if (*lp_rootdir())
+       {
+         if (sys_chroot(lp_rootdir()) == 0)
+           DEBUG(2,("%s changed root to %s\n",timestring(),lp_rootdir()));
+       }
+
+      process();
+      close_sockets();
+    }
+  exit_server("normal exit");
+  return(0);
+}
+
+
diff --git a/source/smbd/smbrun.c b/source/smbd/smbrun.c
new file mode 100644 (file)
index 0000000..df12ae1
--- /dev/null
@@ -0,0 +1,96 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   external program running routine
+   Copyright (C) Andrew Tridgell 1992-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"
+
+
+/*******************************************************************
+close the low 3 fd's and open dev/null in their place
+********************************************************************/
+static void close_fds(void)
+{
+  int fd;
+  int i;
+  close(0); close(1); 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++) {
+    fd = open("/dev/null",O_RDWR,0);
+    if (fd < 0) fd = open("/dev/null",O_WRONLY,0);
+    if (fd != i) return;
+  }
+}
+
+
+/*
+This is a wrapper around the system call to allow commands to run correctly 
+as non root from a program which is switching between root and non-root 
+
+It takes one argument as argv[1] and runs it after becoming a non-root
+user
+*/
+int main(int argc,char *argv[])
+{
+  close_fds();
+
+  if (getuid() != geteuid())
+    {
+      int uid,gid;
+      
+      if (getuid() == 0)
+       uid = geteuid();
+      else
+       uid = getuid();
+      
+      if (getgid() == 0)
+       gid = getegid();
+      else
+       gid = getgid();
+      
+#ifdef USE_SETRES
+      setresgid(0,0,0);
+      setresuid(0,0,0);
+      setresgid(gid,gid,gid);
+      setresuid(uid,uid,uid);      
+#else      
+      setuid(0);
+      seteuid(0);
+      setgid(gid);
+      setegid(gid);
+      setuid(uid);
+      seteuid(uid);
+#endif
+
+      if (getuid() != uid)
+       return(3);
+    }
+
+  if (geteuid() != getuid())
+    return(1);
+
+  if (argc < 2)
+    return(2);
+
+  /* this is to make sure that the system() call doesn't run forever */
+  alarm(30);
+
+  return(system(argv[1]));
+}
diff --git a/source/smbd/trans2.c b/source/smbd/trans2.c
new file mode 100644 (file)
index 0000000..9d02123
--- /dev/null
@@ -0,0 +1,1646 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   SMB transaction2 handling
+   Copyright (C) Jeremy Allison 1994
+
+   Extensively modified by Andrew Tridgell, 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"
+#include "loadparm.h"
+#include "trans2.h"
+
+extern int DEBUGLEVEL;
+extern int Protocol;
+extern connection_struct Connections[];
+extern files_struct Files[];
+extern BOOL case_sensitive;
+extern int Client;
+
+/****************************************************************************
+  Send the required number of replies back.
+  We assume all fields other than the data fields are
+  set correctly for the type of call.
+  HACK ! Always assumes smb_setup field is zero.
+****************************************************************************/
+static int send_trans2_replies(char *outbuf, int bufsize, char *params, 
+                        int paramsize, char *pdata, int datasize)
+{
+  /* As we are using a protocol > LANMAN1 then the maxxmit
+     variable must have been set in the sessetupX call.
+     This takes precedence over the max_xmit field in the
+     global struct. These different max_xmit variables should
+     be merged as this is now too confusing */
+
+  extern int maxxmit;
+  int data_to_send = datasize;
+  int params_to_send = paramsize;
+  int useable_space;
+  char *pp = params;
+  char *pd = pdata;
+  int params_sent_thistime, data_sent_thistime, total_sent_thistime;
+  int alignment_offset = 1;
+
+  /* Initially set the wcnt area to be 10 - this is true for all
+     trans2 replies */
+  set_message(outbuf,10,0,True);
+
+  /* If there genuinely are no parameters or data to send just send
+     the empty packet */
+  if(params_to_send == 0 && data_to_send == 0)
+    {
+      send_smb(Client,outbuf);
+      return 0;
+    }
+
+  /* Space is bufsize minus Netbios over TCP header minus SMB header */
+  /* The + 1 is to align the param and data bytes on an even byte
+     boundary. NT 4.0 Beta needs this to work correctly. */
+  useable_space = bufsize - ((smb_buf(outbuf)+alignment_offset) - outbuf);
+  useable_space = MIN(useable_space, maxxmit); /* XXX is this needed? correct? */
+
+  while( params_to_send || data_to_send)
+    {
+      /* Calculate whether we will totally or partially fill this packet */
+      total_sent_thistime = params_to_send + data_to_send + alignment_offset;
+      total_sent_thistime = MIN(total_sent_thistime, useable_space);
+
+      set_message(outbuf, 10, total_sent_thistime, True);
+
+      /* Set total params and data to be sent */
+      SSVAL(outbuf,smb_tprcnt,paramsize);
+      SSVAL(outbuf,smb_tdrcnt,datasize);
+
+      /* Calculate how many parameters and data we can fit into
+        this packet. Parameters get precedence */
+
+      params_sent_thistime = MIN(params_to_send,useable_space);
+      data_sent_thistime = useable_space - params_sent_thistime;
+      data_sent_thistime = MIN(data_sent_thistime,data_to_send);
+
+      SSVAL(outbuf,smb_prcnt, params_sent_thistime);
+      if(params_sent_thistime == 0)
+       {
+         SSVAL(outbuf,smb_proff,0);
+         SSVAL(outbuf,smb_prdisp,0);
+       } else {
+         /* smb_proff is the offset from the start of the SMB header to the
+            parameter bytes, however the first 4 bytes of outbuf are
+            the Netbios over TCP header. Thus use smb_base() to subtract
+            them from the calculation */
+         SSVAL(outbuf,smb_proff,((smb_buf(outbuf)+alignment_offset) - smb_base(outbuf)));
+         /* Absolute displacement of param bytes sent in this packet */
+         SSVAL(outbuf,smb_prdisp,pp - params);
+       }
+
+      SSVAL(outbuf,smb_drcnt, data_sent_thistime);
+      if(data_sent_thistime == 0)
+       {
+         SSVAL(outbuf,smb_droff,0);
+         SSVAL(outbuf,smb_drdisp, 0);
+       } else {
+         /* The offset of the data bytes is the offset of the
+            parameter bytes plus the number of parameters being sent this time */
+         SSVAL(outbuf,smb_droff,((smb_buf(outbuf)+alignment_offset) - 
+                                 smb_base(outbuf)) + params_sent_thistime);
+         SSVAL(outbuf,smb_drdisp, pd - pdata);
+       }
+
+      /* Copy the param bytes into the packet */
+      if(params_sent_thistime)
+       memcpy((smb_buf(outbuf)+alignment_offset),pp,params_sent_thistime);
+      /* Copy in the data bytes */
+      if(data_sent_thistime)
+       memcpy(smb_buf(outbuf)+alignment_offset+params_sent_thistime,pd,data_sent_thistime);
+
+      DEBUG(9,("t2_rep: params_sent_thistime = %d, data_sent_thistime = %d, useable_space = %d\n",
+              params_sent_thistime, data_sent_thistime, useable_space));
+      DEBUG(9,("t2_rep: params_to_send = %d, data_to_send = %d, paramsize = %d, datasize = %d\n",
+              params_to_send, data_to_send, paramsize, datasize));
+
+      /* Send the packet */
+      send_smb(Client,outbuf);
+
+      pp += params_sent_thistime;
+      pd += data_sent_thistime;
+
+      params_to_send -= params_sent_thistime;
+      data_to_send -= data_sent_thistime;
+
+      /* Sanity check */
+      if(params_to_send < 0 || data_to_send < 0)
+       {
+         DEBUG(2,("send_trans2_replies failed sanity check pts = %d, dts = %d\n!!!",
+                  params_to_send, data_to_send));
+         return -1;
+       }
+    }
+
+  return 0;
+}
+
+
+/****************************************************************************
+  reply to a TRANSACT2_OPEN
+****************************************************************************/
+static int call_trans2open(char *inbuf, char *outbuf, int bufsize, int cnum, 
+                   char **pparams, char **ppdata)
+{
+  char *params = *pparams;
+  int16 open_mode = SVAL(params, 2);
+  int16 open_attr = SVAL(params,6);
+#if 0
+  BOOL return_additional_info = BITSETW(params,0);
+  int16 open_sattr = SVAL(params, 4);
+  time_t open_time = make_unix_date3(params+8);
+#endif
+  int16 open_ofun = SVAL(params,12);
+  int32 open_size = IVAL(params,14);
+  char *pname = &params[28];
+  int16 namelen = strlen(pname)+1;
+
+  pstring fname;
+  int fnum = -1;
+  int unixmode;
+  int size=0,fmode=0,mtime=0,rmode;
+  int32 inode = 0;
+  struct stat sbuf;
+  int smb_action = 0;
+
+  StrnCpy(fname,pname,namelen);
+
+  DEBUG(3,("trans2open %s cnum=%d mode=%d attr=%d ofun=%d size=%d\n",
+          fname,cnum,open_mode, open_attr, open_ofun, open_size));
+
+  /* XXXX we need to handle passed times, sattr and flags */
+
+  unix_convert(fname,cnum);
+    
+  fnum = find_free_file();
+  if (fnum < 0)
+    return(ERROR(ERRSRV,ERRnofids));
+
+  if (!check_name(fname,cnum))
+    return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+  unixmode = unix_mode(cnum,open_attr | aARCH);
+      
+      
+  open_file_shared(fnum,cnum,fname,open_mode,open_ofun,unixmode,
+                  &rmode,&smb_action);
+      
+  if (!Files[fnum].open)
+    return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+  if (fstat(Files[fnum].fd,&sbuf) != 0) {
+    close_file(fnum);
+    return(ERROR(ERRDOS,ERRnoaccess));
+  }
+    
+  size = sbuf.st_size;
+  fmode = dos_mode(cnum,fname,&sbuf);
+  mtime = sbuf.st_mtime;
+  inode = sbuf.st_ino;
+  if (fmode & aDIR) {
+    close_file(fnum);
+    return(ERROR(ERRDOS,ERRnoaccess));
+  }
+
+  /* Realloc the size of parameters and data we will return */
+  params = *pparams = Realloc(*pparams, 28);
+  if(params == NULL)
+    return(ERROR(ERRDOS,ERRnomem));
+
+  bzero(params,28);
+  SSVAL(params,0,fnum);
+  SSVAL(params,2,fmode);
+  put_dos_date2(params,4, mtime);
+  SIVAL(params,8, size);
+  SSVAL(params,12,rmode);
+
+  SSVAL(params,18,smb_action);
+  SIVAL(params,20,inode);
+  /* Send the required number of replies */
+  send_trans2_replies(outbuf, bufsize, params, 28, *ppdata, 0);
+
+  return -1;
+}
+
+/****************************************************************************
+  get a level dependent lanman2 dir entry.
+****************************************************************************/
+static int get_lanman2_dir_entry(int cnum,char *path_mask,int dirtype,int info_level,
+                                int requires_resume_key,
+                                BOOL dont_descend,char **ppdata, 
+                                char *base_data, int space_remaining, 
+                                BOOL *out_of_space,
+                                int *last_name_off)
+{
+  char *dname;
+  BOOL found = False;
+  struct stat sbuf;
+  pstring mask;
+  pstring pathreal;
+  pstring fname;
+  BOOL matched;
+  char *p, *pdata = *ppdata;
+  int reskey=0, prev_dirpos=0;
+  int mode=0;
+  uint32 size=0,len;
+  uint32 mdate=0, adate=0, cdate=0;
+  char *name_ptr;
+  BOOL isrootdir = (strequal(Connections[cnum].dirpath,"./") ||
+                   strequal(Connections[cnum].dirpath,".") ||
+                   strequal(Connections[cnum].dirpath,"/"));
+  BOOL was_8_3;
+
+  *fname = 0;
+  *out_of_space = False;
+
+  if (!Connections[cnum].dirptr)
+    return(False);
+
+  p = strrchr(path_mask,'/');
+  if(p != NULL)
+    {
+      if(p[1] == '\0')
+       strcpy(mask,"*.*");
+      else
+       strcpy(mask, p+1);
+    }
+  else
+    strcpy(mask, path_mask);
+
+  while (!found)
+    {
+      /* Needed if we run out of space */
+      prev_dirpos = TellDir(Connections[cnum].dirptr);
+      dname = ReadDirName(Connections[cnum].dirptr);
+
+      reskey = TellDir(Connections[cnum].dirptr);
+
+      DEBUG(6,("get_lanman2_dir_entry:readdir on dirptr 0x%x now at offset %d\n",
+              Connections[cnum].dirptr,TellDir(Connections[cnum].dirptr)));
+      
+      if (!dname) 
+       return(False);
+
+      matched = False;
+
+      strcpy(fname,dname);      
+
+      if(mask_match(fname, mask, case_sensitive, True))
+       {
+         BOOL isdots = (strequal(fname,"..") || strequal(fname,"."));
+         if (dont_descend && !isdots)
+           continue;
+         
+         if (isrootdir && isdots)
+           continue;
+
+         strcpy(pathreal,Connections[cnum].dirpath);
+         strcat(pathreal,"/");
+         strcat(pathreal,fname);
+         if (sys_stat(pathreal,&sbuf) != 0) 
+           {
+             DEBUG(5,("get_lanman2_dir_entry:Couldn't stat [%s] (%s)\n",pathreal,strerror(errno)));
+             continue;
+           }
+
+         mode = dos_mode(cnum,pathreal,&sbuf);
+
+         if (((mode & ~dirtype) & (aHIDDEN | aSYSTEM | aDIR)) != 0)
+           {         
+             DEBUG(5,("[%s] attribs didn't match %x\n",fname,dirtype));
+             continue;
+           }
+         size = sbuf.st_size;
+         mdate = sbuf.st_mtime;
+         adate = sbuf.st_atime;
+         cdate = sbuf.st_ctime;
+         if(mode & aDIR)
+           size = 0;
+
+         DEBUG(5,("get_lanman2_dir_entry found %s fname=%s\n",pathreal,fname));
+         
+         found = True;
+       }
+    }
+
+
+#ifndef KANJI
+  unix2dos_format(fname, True);
+#endif
+
+  p = pdata;
+  name_ptr = p;
+
+  name_map_mangle(fname,False,SNUM(cnum));
+
+  switch (info_level)
+    {
+    case 1:
+      if(requires_resume_key) {
+       SIVAL(p,0,reskey);
+       p += 4;
+      }
+      put_dos_date2(p,l1_fdateCreation,cdate);
+      put_dos_date2(p,l1_fdateLastAccess,adate);
+      put_dos_date2(p,l1_fdateLastWrite,mdate);
+      SIVAL(p,l1_cbFile,size);
+      SIVAL(p,l1_cbFileAlloc,ROUNDUP(size,1024));
+      SSVAL(p,l1_attrFile,mode);
+      SCVAL(p,l1_cchName,strlen(fname));
+      strcpy(p + l1_achName, fname);
+      name_ptr = p + l1_achName;
+      p += l1_achName + strlen(fname) + 1;
+      break;
+
+    case 2:
+      /* info_level 2 */
+      if(requires_resume_key) {
+       SIVAL(p,0,reskey);
+       p += 4;
+      }
+      put_dos_date2(p,l2_fdateCreation,cdate);
+      put_dos_date2(p,l2_fdateLastAccess,adate);
+      put_dos_date2(p,l2_fdateLastWrite,mdate);
+      SIVAL(p,l2_cbFile,size);
+      SIVAL(p,l2_cbFileAlloc,ROUNDUP(size,1024));
+      SSVAL(p,l2_attrFile,mode);
+      SIVAL(p,l2_cbList,0); /* No extended attributes */
+      SCVAL(p,l2_cchName,strlen(fname));
+      strcpy(p + l2_achName, fname);
+      name_ptr = p + l2_achName;
+      p += l2_achName + strlen(fname) + 1;
+      break;
+
+    case 3:
+      SIVAL(p,0,reskey);
+      put_dos_date2(p,4,cdate);
+      put_dos_date2(p,8,adate);
+      put_dos_date2(p,12,mdate);
+      SIVAL(p,16,size);
+      SIVAL(p,20,ROUNDUP(size,1024));
+      SSVAL(p,24,mode);
+      SIVAL(p,26,4);
+      CVAL(p,30) = strlen(fname);
+      strcpy(p+31, fname);
+      name_ptr = p+31;
+      p += 31 + strlen(fname) + 1;
+      break;
+
+    case 4:
+      if(requires_resume_key) {
+       SIVAL(p,0,reskey);
+       p += 4;
+      }
+      SIVAL(p,0,33+strlen(fname)+1);
+      put_dos_date2(p,4,cdate);
+      put_dos_date2(p,8,adate);
+      put_dos_date2(p,12,mdate);
+      SIVAL(p,16,size);
+      SIVAL(p,20,ROUNDUP(size,1024));
+      SSVAL(p,24,mode);
+      CVAL(p,32) = strlen(fname);
+      strcpy(p + 33, fname);
+      name_ptr = p+33;
+      p += 33 + strlen(fname) + 1;
+      break;
+
+    case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
+      was_8_3 = is_8_3(fname);
+      len = 94+strlen(fname);
+      len = (len + 3) & ~3;
+      SIVAL(p,0,len); p += 4;
+      SIVAL(p,0,reskey); p += 4;
+      put_long_date(p,cdate); p += 8;
+      put_long_date(p,adate); p += 8;
+      put_long_date(p,mdate); p += 8;
+      put_long_date(p,mdate); p += 8;
+      SIVAL(p,0,size); p += 8;
+      SIVAL(p,0,size); p += 8;
+      SIVAL(p,0,mode); p += 4;
+      SIVAL(p,0,strlen(fname)); p += 4;
+      SIVAL(p,0,0); p += 4;
+      if (!was_8_3) {
+#ifndef KANJI
+       strcpy(p+2,unix2dos_format(fname,False));
+#else 
+       strcpy(p+2,fname);
+#endif
+       if (!name_map_mangle(p+2,True,SNUM(cnum)))
+         (p+2)[12] = 0;
+      } else
+       *(p+2) = 0;
+      strupper(p+2);
+      SSVAL(p,0,strlen(p+2));
+      p += 2 + 24;
+      /* name_ptr = p;  */
+      strcpy(p,fname); p += strlen(p);
+      p = pdata + len;
+      break;
+
+    case SMB_FIND_FILE_DIRECTORY_INFO:
+      len = 64+strlen(fname);
+      len = (len + 3) & ~3;
+      SIVAL(p,0,len); p += 4;
+      SIVAL(p,0,reskey); p += 4;
+      put_long_date(p,cdate); p += 8;
+      put_long_date(p,adate); p += 8;
+      put_long_date(p,mdate); p += 8;
+      put_long_date(p,mdate); p += 8;
+      SIVAL(p,0,size); p += 8;
+      SIVAL(p,0,size); p += 8;
+      SIVAL(p,0,mode); p += 4;
+      SIVAL(p,0,strlen(fname)); p += 4;
+      strcpy(p,fname);
+      p = pdata + len;
+      break;
+      
+      
+    case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
+      len = 68+strlen(fname);
+      len = (len + 3) & ~3;
+      SIVAL(p,0,len); p += 4;
+      SIVAL(p,0,reskey); p += 4;
+      put_long_date(p,cdate); p += 8;
+      put_long_date(p,adate); p += 8;
+      put_long_date(p,mdate); p += 8;
+      put_long_date(p,mdate); p += 8;
+      SIVAL(p,0,size); p += 8;
+      SIVAL(p,0,size); p += 8;
+      SIVAL(p,0,mode); p += 4;
+      SIVAL(p,0,strlen(fname)); p += 4;
+      SIVAL(p,0,0); p += 4;
+      strcpy(p,fname);
+      p = pdata + len;
+      break;
+
+    case SMB_FIND_FILE_NAMES_INFO:
+      len = 12+strlen(fname);
+      len = (len + 3) & ~3;
+      SIVAL(p,0,len); p += 4;
+      SIVAL(p,0,reskey); p += 4;
+      SIVAL(p,0,strlen(fname)); p += 4;
+      strcpy(p,fname);
+      p = pdata + len;
+      break;
+
+    default:      
+      return(False);
+    }
+
+
+  if (PTR_DIFF(p,pdata) > space_remaining) {
+    /* Move the dirptr back to prev_dirpos */
+    SeekDir(Connections[cnum].dirptr, prev_dirpos);
+    *out_of_space = True;
+    DEBUG(9,("get_lanman2_dir_entry: out of space\n"));
+    return False; /* Not finished - just out of space */
+  }
+
+  /* Setup the last_filename pointer, as an offset from base_data */
+  *last_name_off = PTR_DIFF(name_ptr,base_data);
+  /* Advance the data pointer to the next slot */
+  *ppdata = p;
+  return(found);
+}
+  
+/****************************************************************************
+  reply to a TRANS2_FINDFIRST
+****************************************************************************/
+static int call_trans2findfirst(char *inbuf, char *outbuf, int bufsize, int cnum, 
+                        char **pparams, char **ppdata)
+{
+  /* We must be careful here that we don't return more than the
+     allowed number of data bytes. If this means returning fewer than
+     maxentries then so be it. We assume that the redirector has
+     enough room for the fixed number of parameter bytes it has
+     requested. */
+  uint32 max_data_bytes = SVAL(inbuf, smb_mdrcnt);
+  char *params = *pparams;
+  char *pdata = *ppdata;
+  int dirtype = SVAL(params,0);
+  int maxentries = SVAL(params,2);
+  BOOL close_after_first = BITSETW(params+4,0);
+  BOOL close_if_end = BITSETW(params+4,1);
+  BOOL requires_resume_key = BITSETW(params+4,2);
+  int info_level = SVAL(params,6);
+  pstring directory;
+  pstring mask;
+  char *p, *wcard;
+  int last_name_off=0;
+  int dptr_num = -1;
+  int numentries = 0;
+  int i;
+  BOOL finished = False;
+  BOOL dont_descend = False;
+  BOOL out_of_space = False;
+  int space_remaining;
+
+  *directory = *mask = 0;
+
+  DEBUG(3,("call_trans2findfirst: dirtype = %d, maxentries = %d, close_after_first=%d, close_if_end = %d requires_resume_key = %d level = %d, max_data_bytes = %d\n",
+          dirtype, maxentries, close_after_first, close_if_end, requires_resume_key,
+          info_level, max_data_bytes));
+  
+  switch (info_level) 
+    {
+    case 1:
+    case 2:
+    case 3:
+    case 4:
+    case SMB_FIND_FILE_DIRECTORY_INFO:
+    case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
+    case SMB_FIND_FILE_NAMES_INFO:
+    case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
+      break;
+    default:
+      return(ERROR(ERRDOS,ERRunknownlevel));
+    }
+
+  strcpy(directory, params + 12); /* Complete directory path with 
+                                    wildcard mask appended */
+
+  DEBUG(5,("path=%s\n",directory));
+
+  unix_convert(directory,cnum);
+  if(!check_name(directory,cnum)) {
+    return(ERROR(ERRDOS,ERRbadpath));
+  }
+
+  p = strrchr(directory,'/');
+  if(p == NULL) {
+    strcpy(mask,directory);
+    strcpy(directory,"./");
+  } else {
+    strcpy(mask,p+1);
+    *p = 0;
+  }
+
+  DEBUG(5,("dir=%s, mask = %s\n",directory, mask));
+
+  pdata = *ppdata = Realloc(*ppdata, max_data_bytes + 1024);
+  if(!*ppdata)
+    return(ERROR(ERRDOS,ERRnomem));
+  bzero(pdata,max_data_bytes);
+
+  /* Realloc the params space */
+  params = *pparams = Realloc(*pparams, 10);
+  if(params == NULL)
+    return(ERROR(ERRDOS,ERRnomem));
+
+  dptr_num = dptr_create(cnum,directory, True ,SVAL(inbuf,smb_pid));
+  if (dptr_num < 0)
+    return(ERROR(ERRDOS,ERRbadpath));
+
+  /* convert the formatted masks */
+  {
+    p = mask;
+    while (*p) {
+      if (*p == '<') *p = '*';
+      if (*p == '>') *p = '?';
+      if (*p == '"') *p = '.';
+      p++;
+    }
+  }
+  
+  /* a special case for 16 bit apps */
+  if (strequal(mask,"????????.???")) strcpy(mask,"*");
+
+  /* handle broken clients that send us old 8.3 format */
+  string_sub(mask,"????????","*");
+  string_sub(mask,".???",".*");
+
+  /* Save the wildcard match and attribs we are using on this directory - 
+     needed as lanman2 assumes these are being saved between calls */
+
+  if(!(wcard = strdup(mask))) {
+    dptr_close(dptr_num);
+    return(ERROR(ERRDOS,ERRnomem));
+  }
+
+  dptr_set_wcard(dptr_num, wcard);
+  dptr_set_attr(dptr_num, dirtype);
+
+  DEBUG(4,("dptr_num is %d, wcard = %s, attr = %d\n",dptr_num, wcard, dirtype));
+
+  /* We don't need to check for VOL here as this is returned by 
+     a different TRANS2 call. */
+  
+  DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n",
+          Connections[cnum].dirpath,lp_dontdescend(SNUM(cnum))));
+  if (in_list(Connections[cnum].dirpath,lp_dontdescend(SNUM(cnum)),case_sensitive))
+    dont_descend = True;
+    
+  p = pdata;
+  space_remaining = max_data_bytes;
+  out_of_space = False;
+
+  for (i=0;(i<maxentries) && !finished && !out_of_space;i++)
+    {
+
+      /* this is a heuristic to avoid seeking the dirptr except when 
+        absolutely necessary. It allows for a filename of about 40 chars */
+      if (space_remaining < DIRLEN_GUESS && numentries > 0)
+       {
+         out_of_space = True;
+         finished = False;
+       }
+      else
+       {
+         finished = 
+           !get_lanman2_dir_entry(cnum,mask,dirtype,info_level,
+                                  requires_resume_key,dont_descend,
+                                  &p,pdata,space_remaining, &out_of_space,
+                                  &last_name_off);
+       }
+
+      if (finished && out_of_space)
+       finished = False;
+
+      if (!finished && !out_of_space)
+       numentries++;
+      space_remaining = max_data_bytes - PTR_DIFF(p,pdata);
+    }
+  
+  /* Check if we can close the dirptr */
+  if(close_after_first || (finished && close_if_end))
+    {
+      dptr_close(dptr_num);
+      DEBUG(5,("call_trans2findfirst - (2) closing dptr_num %d\n", dptr_num));
+      dptr_num = -1;
+    }
+
+  /* At this point pdata points to numentries directory entries. */
+
+  /* Set up the return parameter block */
+  SSVAL(params,0,dptr_num);
+  SSVAL(params,2,numentries);
+  SSVAL(params,4,finished);
+  SSVAL(params,6,0); /* Never an EA error */
+  SSVAL(params,8,last_name_off);
+
+  send_trans2_replies( outbuf, bufsize, params, 10, pdata, PTR_DIFF(p,pdata));
+
+  if ((! *directory) && dptr_path(dptr_num))
+    sprintf(directory,"(%s)",dptr_path(dptr_num));
+
+  DEBUG(4,("%s %s mask=%s directory=%s cnum=%d dirtype=%d numentries=%d\n",
+       timestring(),
+       smb_fn_name(CVAL(inbuf,smb_com)), 
+       mask,directory,cnum,dirtype,numentries));
+
+  return(-1);
+}
+
+
+/****************************************************************************
+  reply to a TRANS2_FINDNEXT
+****************************************************************************/
+static int call_trans2findnext(char *inbuf, char *outbuf, int length, int bufsize,
+                       int cnum, char **pparams, char **ppdata)
+{
+  /* We must be careful here that we don't return more than the
+     allowed number of data bytes. If this means returning fewer than
+     maxentries then so be it. We assume that the redirector has
+     enough room for the fixed number of parameter bytes it has
+     requested. */
+  int max_data_bytes = SVAL(inbuf, smb_mdrcnt);
+  char *params = *pparams;
+  char *pdata = *ppdata;
+  int16 dptr_num = SVAL(params,0);
+  int maxentries = SVAL(params,2);
+  uint16 info_level = SVAL(params,4);
+  uint32 resume_key = IVAL(params,6);
+  BOOL close_after_request = BITSETW(params+10,0);
+  BOOL close_if_end = BITSETW(params+10,1);
+  BOOL requires_resume_key = BITSETW(params+10,2);
+  BOOL continue_bit = BITSETW(params+10,3);
+  pstring mask;
+  pstring directory;
+  char *p;
+  uint16 dirtype;
+  int numentries = 0;
+  int i, last_name_off=0;
+  BOOL finished = False;
+  BOOL dont_descend = False;
+  BOOL out_of_space = False;
+  int space_remaining;
+
+  *mask = *directory = 0;
+
+  DEBUG(3,("call_trans2findnext: dirhandle = %d, max_data_bytes = %d, maxentries = %d, close_after_request=%d, close_if_end = %d requires_resume_key = %d resume_key = %d continue=%d level = %d\n",
+          dptr_num, max_data_bytes, maxentries, close_after_request, close_if_end, 
+          requires_resume_key, resume_key, continue_bit, info_level));
+
+  switch (info_level) 
+    {
+    case 1:
+    case 2:
+    case 3:
+    case 4:
+    case SMB_FIND_FILE_DIRECTORY_INFO:
+    case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
+    case SMB_FIND_FILE_NAMES_INFO:
+    case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
+      break;
+    default:
+      return(ERROR(ERRDOS,ERRunknownlevel));
+    }
+
+  pdata = *ppdata = Realloc( *ppdata, max_data_bytes + 1024);
+  if(!*ppdata)
+    return(ERROR(ERRDOS,ERRnomem));
+  bzero(pdata,max_data_bytes);
+
+  /* Realloc the params space */
+  params = *pparams = Realloc(*pparams, 6*SIZEOFWORD);
+  if(!params)
+    return(ERROR(ERRDOS,ERRnomem));
+
+  /* Check that the dptr is valid */
+  if(!(Connections[cnum].dirptr = dptr_fetch_lanman2(params, dptr_num)))
+    return(ERROR(ERRDOS,ERRnofiles));
+
+  string_set(&Connections[cnum].dirpath,dptr_path(dptr_num));
+
+  /* Get the wildcard mask from the dptr */
+  if((p = dptr_wcard(dptr_num))== NULL) {
+    DEBUG(2,("dptr_num %d has no wildcard\n", dptr_num));
+    return (ERROR(ERRDOS,ERRnofiles));
+  }
+  strcpy(mask, p);
+  strcpy(directory,Connections[cnum].dirpath);
+
+  /* Get the attr mask from the dptr */
+  dirtype = dptr_attr(dptr_num);
+
+  DEBUG(3,("dptr_num is %d, mask = %s, attr = %x, dirptr=(0x%X,%d)\n",
+          dptr_num, mask, dirtype, 
+          Connections[cnum].dirptr,
+          TellDir(Connections[cnum].dirptr)));
+
+  /* We don't need to check for VOL here as this is returned by 
+     a different TRANS2 call. */
+
+  DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n",Connections[cnum].dirpath,lp_dontdescend(SNUM(cnum))));
+  if (in_list(Connections[cnum].dirpath,lp_dontdescend(SNUM(cnum)),case_sensitive))
+    dont_descend = True;
+    
+  p = pdata;
+  space_remaining = max_data_bytes;
+  out_of_space = False;
+
+  /* If we have a resume key - seek to the correct position. */
+  if(requires_resume_key && !continue_bit)
+    SeekDir(Connections[cnum].dirptr, resume_key);
+
+  for (i=0;(i<(int)maxentries) && !finished && !out_of_space ;i++)
+    {
+      /* this is a heuristic to avoid seeking the dirptr except when 
+        absolutely necessary. It allows for a filename of about 40 chars */
+      if (space_remaining < DIRLEN_GUESS && numentries > 0)
+       {
+         out_of_space = True;
+         finished = False;
+       }
+      else
+       {
+         finished = 
+           !get_lanman2_dir_entry(cnum,mask,dirtype,info_level,
+                                  requires_resume_key,dont_descend,
+                                  &p,pdata,space_remaining, &out_of_space,
+                                  &last_name_off);
+       }
+
+      if (finished && out_of_space)
+       finished = False;
+
+      if (!finished && !out_of_space)
+       numentries++;
+      space_remaining = max_data_bytes - PTR_DIFF(p,pdata);
+    }
+  
+  /* Check if we can close the dirptr */
+  if(close_after_request || (finished && close_if_end))
+    {
+      dptr_close(dptr_num); /* This frees up the saved mask */
+      DEBUG(5,("call_trans2findnext: closing dptr_num = %d\n", dptr_num));
+      dptr_num = -1;
+    }
+
+
+  /* Set up the return parameter block */
+  SSVAL(params,0,numentries);
+  SSVAL(params,2,finished);
+  SSVAL(params,4,0); /* Never an EA error */
+  SSVAL(params,6,last_name_off);
+
+  send_trans2_replies( outbuf, bufsize, params, 8, pdata, PTR_DIFF(p,pdata));
+
+  if ((! *directory) && dptr_path(dptr_num))
+    sprintf(directory,"(%s)",dptr_path(dptr_num));
+
+  DEBUG(3,("%s %s mask=%s directory=%s cnum=%d dirtype=%d numentries=%d\n",
+          timestring(),
+          smb_fn_name(CVAL(inbuf,smb_com)), 
+          mask,directory,cnum,dirtype,numentries));
+
+  return(-1);
+}
+
+/****************************************************************************
+  reply to a TRANS2_QFSINFO (query filesystem info)
+****************************************************************************/
+static int call_trans2qfsinfo(char *inbuf, char *outbuf, int length, int bufsize,
+                       int cnum, char **pparams, char **ppdata)
+{
+  char *pdata = *ppdata;
+  char *params = *pparams;
+  uint16 info_level = SVAL(params,0);
+  int data_len;
+  struct stat st;
+  char *vname = volume_label(SNUM(cnum));
+  
+  DEBUG(3,("call_trans2qfsinfo: cnum = %d, level = %d\n", cnum, info_level));
+
+  if(sys_stat(".",&st)!=0) {
+    DEBUG(2,("call_trans2qfsinfo: stat of . failed (%s)\n", strerror(errno)));
+    return (ERROR(ERRSRV,ERRinvdevice));
+  }
+
+  pdata = *ppdata = Realloc(*ppdata, 1024); bzero(pdata,1024);
+
+  switch (info_level) 
+    {
+    case 1:
+      {
+       int dfree,dsize,bsize;
+       data_len = 18;
+       sys_disk_free(".",&bsize,&dfree,&dsize);        
+       SIVAL(pdata,l1_idFileSystem,st.st_dev);
+       SIVAL(pdata,l1_cSectorUnit,bsize/512);
+       SIVAL(pdata,l1_cUnit,dsize);
+       SIVAL(pdata,l1_cUnitAvail,dfree);
+       SSVAL(pdata,l1_cbSector,512);
+       DEBUG(5,("call_trans2qfsinfo : bsize=%d, id=%x, cSectorUnit=%d, cUnit=%d, cUnitAvail=%d, cbSector=%d\n",
+                bsize, st.st_dev, bsize/512, dsize, dfree, 512));
+       break;
+    }
+    case 2:
+    { 
+      /* Return volume name */
+      int volname_len = MIN(strlen(vname),11);
+      data_len = l2_vol_szVolLabel + volname_len + 1;
+      put_dos_date2(pdata,l2_vol_fdateCreation,st.st_ctime);
+      SCVAL(pdata,l2_vol_cch,volname_len);
+      StrnCpy(pdata+l2_vol_szVolLabel,vname,volname_len);
+      DEBUG(5,("call_trans2qfsinfo : time = %x, namelen = %d, name = %s\n",st.st_ctime,volname_len,
+              pdata+l2_vol_szVolLabel));
+      break;
+    }
+    case SMB_QUERY_FS_ATTRIBUTE_INFO:
+      data_len = 12 + 2*strlen(FSTYPE_STRING);
+      SIVAL(pdata,0,0x4006); /* FS ATTRIBUTES == long filenames supported? */
+      SIVAL(pdata,4,128); /* Max filename component length */
+      SIVAL(pdata,8,2*strlen(FSTYPE_STRING));
+      PutUniCode(pdata+12,FSTYPE_STRING);
+      break;
+    case SMB_QUERY_FS_LABEL_INFO:
+      data_len = 4 + strlen(vname);
+      SIVAL(pdata,0,strlen(vname));
+      strcpy(pdata+4,vname);      
+      break;
+    case SMB_QUERY_FS_VOLUME_INFO:      
+      data_len = 17 + strlen(vname);
+      SIVAL(pdata,12,strlen(vname));
+      strcpy(pdata+17,vname);      
+      break;
+    case SMB_QUERY_FS_SIZE_INFO:
+      {
+       int dfree,dsize,bsize;
+       data_len = 24;
+       sys_disk_free(".",&bsize,&dfree,&dsize);        
+       SIVAL(pdata,0,dsize);
+       SIVAL(pdata,8,dfree);
+       SIVAL(pdata,16,bsize/512);
+       SIVAL(pdata,20,512);
+      }
+      break;
+    case SMB_QUERY_FS_DEVICE_INFO:
+      data_len = 8;
+      SIVAL(pdata,0,0); /* dev type */
+      SIVAL(pdata,4,0); /* characteristics */
+      break;
+    default:
+      return(ERROR(ERRDOS,ERRunknownlevel));
+    }
+
+
+  send_trans2_replies( outbuf, bufsize, params, 0, pdata, data_len);
+
+  DEBUG(4,("%s %s info_level =%d\n",timestring(),smb_fn_name(CVAL(inbuf,smb_com)), info_level));
+
+  return -1;
+}
+
+/****************************************************************************
+  reply to a TRANS2_SETFSINFO (set filesystem info)
+****************************************************************************/
+static int call_trans2setfsinfo(char *inbuf, char *outbuf, int length, int bufsize,
+                       int cnum, char **pparams, char **ppdata)
+{
+  /* Just say yes we did it - there is nothing that
+     can be set here so it doesn't matter. */
+  int outsize;
+  DEBUG(3,("call_trans2setfsinfo\n"));
+
+  if (!CAN_WRITE(cnum))
+    return(ERROR(ERRSRV,ERRaccess));
+
+  outsize = set_message(outbuf,10,0,True);
+
+  return outsize;
+}
+
+/****************************************************************************
+  reply to a TRANS2_QFILEINFO (query file info by fileid)
+****************************************************************************/
+static int call_trans2qfilepathinfo(char *inbuf, char *outbuf, int length, 
+                                   int bufsize,int cnum,
+                                   char **pparams,char **ppdata,
+                                   int total_data)
+{
+  char *params = *pparams;
+  char *pdata = *ppdata;
+  uint16 tran_call = SVAL(inbuf, smb_setup0);
+  uint16 info_level;
+  int mode=0;
+  int size=0;
+  unsigned int data_size;
+  struct stat sbuf;
+  pstring fname1;
+  char *fname;
+  char *p;
+  int l,pos;
+
+
+  if (tran_call == TRANSACT2_QFILEINFO) {
+    int16 fnum = SVAL(params,0);
+    info_level = SVAL(params,2);
+
+    CHECK_FNUM(fnum,cnum);
+    CHECK_ERROR(fnum);
+
+    fname = Files[fnum].name;
+    if (fstat(Files[fnum].fd,&sbuf) != 0) {
+      DEBUG(3,("fstat of fnum %d failed (%s)\n",fnum, strerror(errno)));
+      return(UNIXERROR(ERRDOS,ERRbadfid));
+    }
+    pos = lseek(Files[fnum].fd,0,SEEK_CUR);
+  } else {
+    /* qpathinfo */
+    info_level = SVAL(params,0);
+    fname = &fname1[0];
+    strcpy(fname,&params[6]);
+    unix_convert(fname,cnum);
+    if (!check_name(fname,cnum) || sys_stat(fname,&sbuf)) {
+      DEBUG(3,("fileinfo of %s failed (%s)\n",fname,strerror(errno)));
+      return(UNIXERROR(ERRDOS,ERRbadpath));
+    }
+    pos = 0;
+  }
+
+
+  DEBUG(3,("call_trans2qfilepathinfo %s level=%d call=%d total_data=%d\n",
+          fname,info_level,tran_call,total_data));
+
+  p = strrchr(fname,'/'); 
+  if (!p) 
+    p = fname;
+  else
+    p++;
+  l = strlen(p);  
+  mode = dos_mode(cnum,fname,&sbuf);
+  size = sbuf.st_size;
+  if (mode & aDIR) size = 0;
+  
+  params = *pparams = Realloc(*pparams,2); bzero(params,2);
+  data_size = 1024;
+  pdata = *ppdata = Realloc(*ppdata, data_size); 
+
+  if (total_data > 0 && IVAL(pdata,0) == total_data) {
+    /* uggh, EAs for OS2 */
+    DEBUG(4,("Rejecting EA request with total_data=%d\n",total_data));
+#if 0
+    SSVAL(params,0,ERROR_EAS_NOT_SUPPORTED);
+    send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0);
+    return(-1);
+#else
+    return(ERROR(ERRDOS,ERROR_EAS_NOT_SUPPORTED));
+#endif
+  }
+
+  bzero(pdata,data_size);
+
+  switch (info_level) 
+    {
+    case 1:
+    case 2:
+      data_size = (info_level==1?22:26);
+      put_dos_date2(pdata,l1_fdateCreation,sbuf.st_ctime);
+      put_dos_date2(pdata,l1_fdateLastAccess,sbuf.st_atime);
+      put_dos_date2(pdata,l1_fdateLastWrite,sbuf.st_mtime);
+      SIVAL(pdata,l1_cbFile,size);
+      SIVAL(pdata,l1_cbFileAlloc,ROUNDUP(size,1024));
+      SSVAL(pdata,l1_attrFile,mode);
+      SIVAL(pdata,l1_attrFile+2,4); /* this is what OS2 does */
+      break;
+
+    case 3:
+      data_size = 24;
+      put_dos_date2(pdata,0,sbuf.st_ctime);
+      put_dos_date2(pdata,4,sbuf.st_atime);
+      put_dos_date2(pdata,8,sbuf.st_mtime);
+      SIVAL(pdata,12,size);
+      SIVAL(pdata,16,ROUNDUP(size,1024));
+      SIVAL(pdata,20,mode);
+      break;
+
+    case 4:
+      data_size = 4;
+      SIVAL(pdata,0,data_size);
+      break;
+
+    case 6:
+      return(ERROR(ERRDOS,ERRbadfunc)); /* os/2 needs this */      
+
+    case SMB_QUERY_FILE_BASIC_INFO:
+      data_size = 36;
+      put_long_date(pdata,sbuf.st_ctime);
+      put_long_date(pdata+8,sbuf.st_atime);
+      put_long_date(pdata+16,sbuf.st_mtime);
+      put_long_date(pdata+24,sbuf.st_mtime);
+      SIVAL(pdata,32,mode);
+      break;
+
+    case SMB_QUERY_FILE_STANDARD_INFO:
+      data_size = 22;
+      SIVAL(pdata,0,size);
+      SIVAL(pdata,8,size);
+      SIVAL(pdata,16,sbuf.st_nlink);
+      CVAL(pdata,20) = 0;
+      CVAL(pdata,21) = (mode&aDIR)?1:0;
+      break;
+
+    case SMB_QUERY_FILE_EA_INFO:
+      data_size = 4;
+      break;
+
+    case SMB_QUERY_FILE_NAME_INFO:
+    case SMB_QUERY_FILE_ALT_NAME_INFO:
+      data_size = 4 + l;
+      SIVAL(pdata,0,l);
+      strcpy(pdata+4,fname);
+      break;
+    case SMB_QUERY_FILE_ALLOCATION_INFO:
+    case SMB_QUERY_FILE_END_OF_FILEINFO:
+      data_size = 8;
+      SIVAL(pdata,0,size);
+      break;
+
+    case SMB_QUERY_FILE_ALL_INFO:
+      put_long_date(pdata,sbuf.st_ctime);
+      put_long_date(pdata+8,sbuf.st_atime);
+      put_long_date(pdata+16,sbuf.st_mtime);
+      put_long_date(pdata+24,sbuf.st_mtime);
+      SIVAL(pdata,32,mode);
+      pdata += 40;
+      SIVAL(pdata,0,size);
+      SIVAL(pdata,8,size);
+      SIVAL(pdata,16,sbuf.st_nlink);
+      CVAL(pdata,20) = 0;
+      CVAL(pdata,21) = (mode&aDIR)?1:0;
+      pdata += 24;
+      pdata += 8; /* index number */
+      pdata += 4; /* EA info */
+      if (mode & aRONLY)
+       SIVAL(pdata,0,0xA9);
+      else
+       SIVAL(pdata,0,0xd01BF);
+      pdata += 4;
+      SIVAL(pdata,0,pos); /* current offset */
+      pdata += 8;
+      SIVAL(pdata,0,mode); /* is this the right sort of mode info? */
+      pdata += 4;
+      pdata += 4; /* alignment */
+      SIVAL(pdata,0,l);
+      strcpy(pdata+4,fname);
+      pdata += 4 + l;
+      data_size = PTR_DIFF(pdata,(*ppdata));
+      break;
+
+    case SMB_QUERY_FILE_STREAM_INFO:
+      data_size = 24 + l;
+      SIVAL(pdata,0,pos);
+      SIVAL(pdata,4,size);
+      SIVAL(pdata,12,size);
+      SIVAL(pdata,20,l);       
+      strcpy(pdata+24,fname);
+      break;
+    default:
+      return(ERROR(ERRDOS,ERRunknownlevel));
+    }
+
+  send_trans2_replies( outbuf, bufsize, params, 2, *ppdata, data_size);
+
+  return(-1);
+}
+
+/****************************************************************************
+  reply to a TRANS2_SETFILEINFO (set file info by fileid)
+****************************************************************************/
+static int call_trans2setfilepathinfo(char *inbuf, char *outbuf, int length, 
+                                     int bufsize, int cnum, char **pparams, 
+                                     char **ppdata, int total_data)
+{
+  char *params = *pparams;
+  char *pdata = *ppdata;
+  uint16 tran_call = SVAL(inbuf, smb_setup0);
+  uint16 info_level;
+  int mode=0;
+  int size=0;
+  struct utimbuf tvs;
+  struct stat st;
+  pstring fname1;
+  char *fname;
+  int fd = -1;
+
+  if (!CAN_WRITE(cnum))
+    return(ERROR(ERRSRV,ERRaccess));
+
+  if (tran_call == TRANSACT2_SETFILEINFO) {
+    int16 fnum = SVAL(params,0);
+    info_level = SVAL(params,2);    
+
+    CHECK_FNUM(fnum,cnum);
+    CHECK_ERROR(fnum);
+
+    fname = Files[fnum].name;
+    fd = Files[fnum].fd;
+
+    if(fstat(fd,&st)!=0) {
+      DEBUG(3,("fstat of %s failed (%s)\n", fname, strerror(errno)));
+      return(ERROR(ERRDOS,ERRbadpath));
+    }
+  } else {
+    /* set path info */
+    info_level = SVAL(params,0);    
+    fname = fname1;
+    strcpy(fname,&params[6]);
+    unix_convert(fname,cnum);
+    if(!check_name(fname, cnum))
+      return(ERROR(ERRDOS,ERRbadpath));
+    
+    if(sys_stat(fname,&st)!=0) {
+      DEBUG(3,("stat of %s failed (%s)\n", fname, strerror(errno)));
+      return(ERROR(ERRDOS,ERRbadpath));
+    }    
+  }
+
+  DEBUG(3,("call_trans2setfilepathinfo(%d) %s info_level=%d totdata=%d\n",
+          tran_call,fname,info_level,total_data));
+
+  /* Realloc the parameter and data sizes */
+  params = *pparams = Realloc(*pparams,2); SSVAL(params,0,0);
+  if(params == NULL)
+    return(ERROR(ERRDOS,ERRnomem));
+
+  size = st.st_size;
+  tvs.modtime = st.st_mtime;
+  tvs.actime = st.st_atime;
+  mode = dos_mode(cnum,fname,&st);
+
+  if (total_data > 0 && IVAL(pdata,0) == total_data) {
+    /* uggh, EAs for OS2 */
+    DEBUG(4,("Rejecting EA request with total_data=%d\n",total_data));
+    SSVAL(params,0,ERROR_EAS_NOT_SUPPORTED);
+
+    send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0);
+  
+    return(-1);    
+  }
+
+  switch (info_level)
+    {
+    case 1:
+      tvs.actime = make_unix_date2(pdata+l1_fdateLastAccess);
+      tvs.modtime = make_unix_date2(pdata+l1_fdateLastWrite);
+      mode = SVAL(pdata,l1_attrFile);
+      size = IVAL(pdata,l1_cbFile);
+      break;
+
+    case 2:
+      tvs.actime = make_unix_date2(pdata+l1_fdateLastAccess);
+      tvs.modtime = make_unix_date2(pdata+l1_fdateLastWrite);
+      mode = SVAL(pdata,l1_attrFile);
+      size = IVAL(pdata,l1_cbFile);
+      break;
+
+    case 3:
+      tvs.actime = make_unix_date2(pdata+8);
+      tvs.modtime = make_unix_date2(pdata+12);
+      size = IVAL(pdata,16);
+      mode = IVAL(pdata,24);
+      break;
+
+    case 4:
+      tvs.actime = make_unix_date2(pdata+8);
+      tvs.modtime = make_unix_date2(pdata+12);
+      size = IVAL(pdata,16);
+      mode = IVAL(pdata,24);
+      break;
+
+    case SMB_SET_FILE_BASIC_INFO:
+      pdata += 8;              /* create time */
+      tvs.actime = interpret_long_date(pdata); pdata += 8;
+      tvs.modtime=MAX(interpret_long_date(pdata),interpret_long_date(pdata+8));
+      pdata += 16;
+      mode = IVAL(pdata,0);
+      break;
+
+    case SMB_SET_FILE_END_OF_FILE_INFO:
+      if (IVAL(pdata,4) != 0)  /* more than 32 bits? */
+       return(ERROR(ERRDOS,ERRunknownlevel));
+      size = IVAL(pdata,0);
+      break;
+
+    case SMB_SET_FILE_DISPOSITION_INFO: /* not supported yet */
+    case SMB_SET_FILE_ALLOCATION_INFO: /* not supported yet */
+    default:
+      return(ERROR(ERRDOS,ERRunknownlevel));
+    }
+
+
+  if (!tvs.actime) tvs.actime = st.st_atime;
+  if (!tvs.modtime) tvs.modtime = st.st_mtime;
+  if (!size) size = st.st_size;
+
+  /* Try and set the times, size and mode of this file - if they are different 
+   from the current values */
+  if(st.st_mtime != tvs.modtime || st.st_atime != tvs.actime) {
+    if(sys_utime(fname, &tvs)!=0)
+      return(ERROR(ERRDOS,ERRnoaccess));
+  }
+  if(mode != dos_mode(cnum,fname,&st) && dos_chmod(cnum,fname,mode,NULL)) {
+    DEBUG(2,("chmod of %s failed (%s)\n", fname, strerror(errno)));
+    return(ERROR(ERRDOS,ERRnoaccess));
+  }
+  if(size != st.st_size) {
+    if (fd == -1) {
+      fd = sys_open(fname,O_RDWR,0);
+      if (fd == -1)
+       return(ERROR(ERRDOS,ERRbadpath));
+      set_filelen(fd, size);
+      close(fd);
+    } else {
+      set_filelen(fd, size);
+    }
+  }
+
+  SSVAL(params,0,0);
+
+  send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0);
+  
+  return(-1);
+}
+
+/****************************************************************************
+  reply to a TRANS2_MKDIR (make directory with extended attributes).
+****************************************************************************/
+static int call_trans2mkdir(char *inbuf, char *outbuf, int length, int bufsize,
+                       int cnum, char **pparams, char **ppdata)
+{
+  char *params = *pparams;
+  pstring directory;
+  int ret = -1;
+
+  if (!CAN_WRITE(cnum))
+    return(ERROR(ERRSRV,ERRaccess));
+
+  strcpy(directory, &params[4]);
+
+  DEBUG(3,("call_trans2mkdir : name = %s\n", directory));
+
+  unix_convert(directory,cnum);
+  if (check_name(directory,cnum))
+    ret = sys_mkdir(directory,unix_mode(cnum,aDIR));
+  
+  if(ret < 0)
+    {
+      DEBUG(5,("call_trans2mkdir error (%s)\n", strerror(errno)));
+      return(UNIXERROR(ERRDOS,ERRnoaccess));
+    }
+
+  /* Realloc the parameter and data sizes */
+  params = *pparams = Realloc(*pparams,2);
+  if(params == NULL)
+    return(ERROR(ERRDOS,ERRnomem));
+
+  SSVAL(params,0,0);
+
+  send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0);
+  
+  return(-1);
+}
+
+/****************************************************************************
+  reply to a TRANS2_FINDNOTIFYFIRST (start monitoring a directory for changes)
+  We don't actually do this - we just send a null response.
+****************************************************************************/
+static int call_trans2findnotifyfirst(char *inbuf, char *outbuf, int length, int bufsize,
+                       int cnum, char **pparams, char **ppdata)
+{
+  static uint16 fnf_handle = 257;
+  char *params = *pparams;
+  uint16 info_level = SVAL(params,4);
+
+  DEBUG(3,("call_trans2findnotifyfirst - info_level %d\n", info_level));
+
+  switch (info_level) 
+    {
+    case 1:
+    case 2:
+      break;
+    default:
+      return(ERROR(ERRDOS,ERRunknownlevel));
+    }
+
+  /* Realloc the parameter and data sizes */
+  params = *pparams = Realloc(*pparams,6);
+  if(params == NULL)
+    return(ERROR(ERRDOS,ERRnomem));
+
+  SSVAL(params,0,fnf_handle);
+  SSVAL(params,2,0); /* No changes */
+  SSVAL(params,4,0); /* No EA errors */
+
+  fnf_handle++;
+
+  if(fnf_handle == 0)
+    fnf_handle = 257;
+
+  send_trans2_replies(outbuf, bufsize, params, 6, *ppdata, 0);
+  
+  return(-1);
+}
+
+/****************************************************************************
+  reply to a TRANS2_FINDNOTIFYNEXT (continue monitoring a directory for 
+  changes). Currently this does nothing.
+****************************************************************************/
+static int call_trans2findnotifynext(char *inbuf, char *outbuf, int length, int bufsize,
+                       int cnum, char **pparams, char **ppdata)
+{
+  char *params = *pparams;
+
+  DEBUG(3,("call_trans2findnotifynext\n"));
+
+  /* Realloc the parameter and data sizes */
+  params = *pparams = Realloc(*pparams,4);
+  if(params == NULL)
+    return(ERROR(ERRDOS,ERRnomem));
+
+  SSVAL(params,0,0); /* No changes */
+  SSVAL(params,2,0); /* No EA errors */
+
+  send_trans2_replies(outbuf, bufsize, params, 4, *ppdata, 0);
+  
+  return(-1);
+}
+
+/****************************************************************************
+  reply to a SMBfindclose (stop trans2 directory search)
+****************************************************************************/
+int reply_findclose(char *inbuf,char *outbuf,int length,int bufsize)
+{
+  int cnum;
+  int outsize = 0;
+  uint16 dptr_num=SVAL(inbuf,smb_vwv0);
+
+  cnum = SVAL(inbuf,smb_tid);
+
+  DEBUG(3,("reply_findclose, cnum = %d, dptr_num = %d\n", cnum, dptr_num));
+
+  dptr_close(dptr_num);
+
+  outsize = set_message(outbuf,0,0,True);
+
+  DEBUG(3,("%s SMBfindclose cnum=%d, dptr_num = %d\n",timestring(),cnum,dptr_num));
+
+  return(outsize);
+}
+
+/****************************************************************************
+  reply to a SMBfindnclose (stop FINDNOTIFYFIRST directory search)
+****************************************************************************/
+int reply_findnclose(char *inbuf,char *outbuf,int length,int bufsize)
+{
+  int cnum;
+  int outsize = 0;
+  int dptr_num= -1;
+
+  cnum = SVAL(inbuf,smb_tid);
+  dptr_num = SVAL(inbuf,smb_vwv0);
+
+  DEBUG(3,("reply_findnclose, cnum = %d, dptr_num = %d\n", cnum, dptr_num));
+
+  /* We never give out valid handles for a 
+     findnotifyfirst - so any dptr_num is ok here. 
+     Just ignore it. */
+
+  outsize = set_message(outbuf,0,0,True);
+
+  DEBUG(3,("%s SMB_findnclose cnum=%d, dptr_num = %d\n",timestring(),cnum,dptr_num));
+
+  return(outsize);
+}
+
+
+/****************************************************************************
+  reply to a SMBtranss2 - just ignore it!
+****************************************************************************/
+int reply_transs2(char *inbuf,char *outbuf,int length,int bufsize)
+{
+  DEBUG(4,("Ignoring transs2 of length %d\n",length));
+  return(-1);
+}
+
+/****************************************************************************
+  reply to a SMBtrans2
+****************************************************************************/
+int reply_trans2(char *inbuf,char *outbuf,int length,int bufsize)
+{
+  int outsize = 0;
+  int cnum = SVAL(inbuf,smb_tid);
+  unsigned int total_params = SVAL(inbuf, smb_tpscnt);
+  unsigned int total_data =SVAL(inbuf, smb_tdscnt);
+#if 0
+  unsigned int max_param_reply = SVAL(inbuf, smb_mprcnt);
+  unsigned int max_data_reply = SVAL(inbuf, smb_mdrcnt);
+  unsigned int max_setup_fields = SVAL(inbuf, smb_msrcnt);
+  BOOL close_tid = BITSETW(inbuf+smb_flags,0);
+  BOOL no_final_response = BITSETW(inbuf+smb_flags,1);
+  int32 timeout = IVALS(inbuf,smb_timeout);
+#endif
+  unsigned int suwcnt = SVAL(inbuf, smb_suwcnt);
+  unsigned int tran_call = SVAL(inbuf, smb_setup0);
+  char *params = NULL, *data = NULL;
+  int num_params, num_params_sofar, num_data, num_data_sofar;
+
+  outsize = set_message(outbuf,0,0,True);
+
+  /* All trans2 messages we handle have smb_sucnt == 1 - ensure this
+     is so as a sanity check */
+  if(suwcnt != 1 )
+    {
+      DEBUG(2,("Invalid smb_sucnt in trans2 call\n"));
+      return(ERROR(ERRSRV,ERRerror));
+    }
+    
+  /* Allocate the space for the maximum needed parameters and data */
+  if (total_params > 0)
+    params = (char *)malloc(total_params);
+  if (total_data > 0)
+    data = (char *)malloc(total_data);
+  
+  if ((total_params && !params)  || (total_data && !data))
+    {
+      DEBUG(2,("Out of memory in reply_trans2\n"));
+      return(ERROR(ERRDOS,ERRnomem));
+    }
+
+  /* Copy the param and data bytes sent with this request into
+     the params buffer */
+  num_params = num_params_sofar = SVAL(inbuf,smb_pscnt);
+  num_data = num_data_sofar = SVAL(inbuf, smb_dscnt);
+
+  memcpy( params, smb_base(inbuf) + SVAL(inbuf, smb_psoff), num_params);
+  memcpy( data, smb_base(inbuf) + SVAL(inbuf, smb_dsoff), num_data);
+
+  if(num_data_sofar < total_data || num_params_sofar < total_params)
+    {
+    /* We need to send an interim response then receive the rest
+       of the parameter/data bytes */
+      outsize = set_message(outbuf,0,0,True);
+      send_smb(Client,outbuf);
+
+      while( num_data_sofar < total_data || num_params_sofar < total_params)
+       {
+         receive_smb(Client,inbuf, 0);
+         
+         /* Ensure this is still a trans2 packet (sanity check) */
+         if(CVAL(inbuf, smb_com) != SMBtranss2)
+           {
+             outsize = set_message(outbuf,0,0,True);
+             DEBUG(2,("Invalid secondary trans2 packet\n"));
+             free(params);
+             free(data);
+             return(ERROR(ERRSRV,ERRerror));
+           }
+      
+         /* Revise total_params and total_data in case they have changed downwards */
+         total_params = SVAL(inbuf, smb_tpscnt);
+         total_data = SVAL(inbuf, smb_tdscnt);
+         num_params_sofar += (num_params = SVAL(inbuf,smb_spscnt));
+         num_data_sofar += ( num_data = SVAL(inbuf, smb_sdscnt));
+         memcpy( &params[ SVAL(inbuf, smb_spsdisp)], 
+                smb_base(inbuf) + SVAL(inbuf, smb_spsoff), num_params);
+         memcpy( &data[SVAL(inbuf, smb_sdsdisp)],
+                smb_base(inbuf)+ SVAL(inbuf, smb_sdsoff), num_data);
+       }
+    }
+
+  if (Protocol >= PROTOCOL_NT1) {
+    uint16 flg2 = SVAL(outbuf,smb_flg2);
+    SSVAL(outbuf,smb_flg2,flg2 | 0x40); /* IS_LONG_NAME */
+  }
+
+  /* Now we must call the relevant TRANS2 function */
+  switch(tran_call) 
+    {
+    case TRANSACT2_OPEN:
+      outsize = call_trans2open(inbuf, outbuf, bufsize, cnum, &params, &data);
+      break;
+    case TRANSACT2_FINDFIRST:
+      outsize = call_trans2findfirst(inbuf, outbuf, bufsize, cnum, &params, &data);
+      break;
+    case TRANSACT2_FINDNEXT:
+      outsize = call_trans2findnext(inbuf, outbuf, length, bufsize, cnum, &params, &data);
+      break;
+    case TRANSACT2_QFSINFO:
+      outsize = call_trans2qfsinfo(inbuf, outbuf, length, bufsize, cnum, &params, &data);
+      break;
+    case TRANSACT2_SETFSINFO:
+      outsize = call_trans2setfsinfo(inbuf, outbuf, length, bufsize, cnum, &params, &data);
+      break;
+    case TRANSACT2_QPATHINFO:
+    case TRANSACT2_QFILEINFO:
+      outsize = call_trans2qfilepathinfo(inbuf, outbuf, length, bufsize, cnum, &params, &data, total_data);
+      break;
+    case TRANSACT2_SETPATHINFO:
+    case TRANSACT2_SETFILEINFO:
+      outsize = call_trans2setfilepathinfo(inbuf, outbuf, length, bufsize, cnum, &params, &data, total_data);
+      break;
+    case TRANSACT2_FINDNOTIFYFIRST:
+      outsize = call_trans2findnotifyfirst(inbuf, outbuf, length, bufsize, cnum, &params, &data);
+      break;
+    case TRANSACT2_FINDNOTIFYNEXT:
+      outsize = call_trans2findnotifynext(inbuf, outbuf, length, bufsize, cnum, &params, &data);
+      break;
+    case TRANSACT2_MKDIR:
+      outsize = call_trans2mkdir(inbuf, outbuf, length, bufsize, cnum, &params, &data);
+      break;
+    default:
+      /* Error in request */
+      DEBUG(2,("%s Unknown request %d in trans2 call\n",timestring(), tran_call));
+      if(params)
+       free(params);
+      if(data)
+       free(data);
+      return (ERROR(ERRSRV,ERRerror));
+    }
+
+  /* As we do not know how many data packets will need to be
+     returned here the various call_trans2xxxx calls
+     must send their own. Thus a call_trans2xxx routine only
+     returns a value other than -1 when it wants to send
+     an error packet. 
+  */
+
+  if(params)
+    free(params);
+  if(data)
+    free(data);
+  return outsize; /* If a correct response was needed the call_trans2xxx 
+                    calls have already sent it. If outsize != -1 then it is
+                    returning an error packet. */
+}
diff --git a/source/smbd/vt_mode.c b/source/smbd/vt_mode.c
new file mode 100644 (file)
index 0000000..83b62a3
--- /dev/null
@@ -0,0 +1,496 @@
+/* vt_mode.c */
+/*
+support vtp-sessions
+
+written by Christian A. Lademann <cal@zls.com>
+*/
+
+/*
+02.05.95:cal:ported to samba-1.9.13
+*/
+
+#define        __vt_mode_c__
+
+
+/* #include    <stdio.h> */
+/* #include    <fcntl.h> */
+/* #include    <sys/types.h> */
+/* #include    <unistd.h> */
+/* #include    <signal.h> */
+/* #include    <errno.h> */
+/* #include    <ctype.h> */
+/* #include    <utmp.h> */
+/* #include    <sys/param.h> */
+/* #include    <sys/ioctl.h> */
+/* #include    <stdlib.h> */
+/* #include    <string.h> */
+
+#include       "includes.h"
+#include       "vt_mode.h"
+#include       <utmp.h>
+
+#ifdef SCO
+       extern char     *strdup();
+#endif
+
+extern int Client;
+
+#ifdef LINUX
+#      define  HAS_VTY
+#endif
+
+#ifdef SCO
+#      define  HAS_PTY
+#      define  HAS_VTY
+
+#      include <sys/tty.h>
+#endif
+
+extern int     DEBUGLEVEL;
+extern char    *InBuffer, *OutBuffer;
+extern int     done_become_user;
+
+char   master_name [64], slave_name [64];
+int            master, slave, i, o, e;
+
+int            ms_type = MS_NONE,
+               ms_poll = 0;
+
+
+/*
+VT_Check: test incoming packet for "vtp" or "iVT1\0"
+*/
+int    VT_Check(buffer)
+char   *buffer;
+{
+       DEBUG(3,("Checking packet: <%10s...>\n", buffer+4));
+       if((strncmp(buffer+4, "vtp", 3) == 0 && smb_len(buffer) == 3) || (strncmp(buffer+4, "iVT1\0", 5) == 0 && smb_len(buffer) == 5))
+               return(1);
+       else
+               return(0);
+}
+
+
+/*
+VT_Start_utmp: prepare /etc/utmp for /bin/login
+*/
+VT_Start_utmp()
+{
+       struct utmp     u, *v;
+       char            *tt;
+
+
+       setutent();
+
+       strcpy(u.ut_line, VT_Line);
+
+       if((v = getutline(&u)) == NULL) {
+               if(strncmp(VT_Line, "tty", 3) == 0)
+                       tt = VT_Line + 3;
+               else if(strlen(VT_Line) > 4)
+                       tt = VT_Line + strlen(VT_Line) - 4;
+               else
+                       tt = VT_Line;
+
+               strcpy(u.ut_id, tt);
+               u.ut_time = time((time_t*)0);
+       }
+
+       strcpy(u.ut_user, "LOGIN");
+       strcpy(u.ut_line, VT_Line);
+       u.ut_pid = getpid();
+       u.ut_type = LOGIN_PROCESS;
+       pututline(&u);
+
+       endutent();
+
+       return(0);
+}
+
+
+/*
+VT_Stop_utmp: prepare /etc/utmp for other processes
+*/
+VT_Stop_utmp()
+{
+       struct utmp     u, *v;
+
+
+       if(VT_Line != NULL) {
+               setutent();
+
+               strcpy(u.ut_line, VT_Line);
+
+               if((v = getutline(&u)) != NULL) {
+                       strcpy(v->ut_user, "");
+                       v->ut_type = DEAD_PROCESS;
+                       v->ut_time = time((time_t*)0);
+                       pututline(v);
+               }
+
+               endutent();
+       }
+
+       return(0);
+}
+
+
+/*
+VT_AtExit: Things to do when the program exits
+*/
+void   VT_AtExit()
+{
+       if(VT_ChildPID > 0) {
+               kill(VT_ChildPID, SIGHUP);
+               (void)wait(NULL);
+       }
+
+       VT_Stop_utmp();
+}
+
+
+/*
+VT_SigCLD: signalhandler for SIGCLD: set flag if child-process died
+*/
+void   VT_SigCLD(sig)
+int    sig;
+{
+       if(wait(NULL) == VT_ChildPID)
+               VT_ChildDied = True;
+       else
+               signal(SIGCLD, VT_SigCLD);
+}
+
+
+/*
+VT_SigEXIT: signalhandler for signals that cause the process to exit
+*/
+void   VT_SigEXIT(sig)
+int    sig;
+{
+       VT_AtExit();
+
+       exit(1);
+}
+
+
+/*
+VT_Start: initialize vt-specific data, alloc pty, spawn shell and send ACK
+*/
+int    VT_Start()
+{
+       char    OutBuf [64], *X, *Y;
+
+
+       ms_type = MS_NONE;
+       master = slave = -1;
+
+#ifdef HAS_VTY
+#ifdef LINUX
+#      define  MASTER_TMPL     "/dev/pty  "
+#      define  SLAVE_TMPL      "/dev/tty  "
+#      define  LETTER1         "pqrs"
+#      define  POS1            8
+#      define  LETTER2         "0123456789abcdef"
+#      define  POS2            9
+#endif
+
+#ifdef SCO
+#      define  MASTER_TMPL     "/dev/ptyp_  "
+#      define  SLAVE_TMPL      "/dev/ttyp_  "
+#      define  LETTER1         "0123456"
+#      define  POS1            10
+#      define  LETTER2         "0123456789abcdef"
+#      define  POS2            11
+#endif
+
+       if(ms_poll == MS_VTY || ms_poll == 0) {
+               strcpy(master_name, MASTER_TMPL);
+               strcpy(slave_name, SLAVE_TMPL);
+
+               for(X = LETTER1; *X && master < 0; X++)
+                       for(Y = LETTER2; *Y && master < 0; Y++) {
+                               master_name [POS1] = *X;
+                               master_name [POS2] = *Y;
+                               if((master = open(master_name, O_RDWR)) >= 0) {
+                                       slave_name [POS1] = *X;
+                                       slave_name [POS2] = *Y;
+                                       if((slave = open(slave_name, O_RDWR)) < 0)
+                                               close(master);
+                               }
+                       }
+
+               if(master >= 0 && slave >= 0)
+                       ms_type = MS_VTY;
+       }
+
+#      undef   MASTER_TMPL
+#      undef   SLAVE_TMPL
+#      undef   LETTER1
+#      undef   LETTER2
+#      undef   POS1
+#      undef   POS2
+#endif
+
+
+#ifdef HAS_PTY
+#ifdef SCO
+#      define  MASTER_TMPL     "/dev/ptyp%d"
+#      define  SLAVE_TMPL      "/dev/ttyp%d"
+#      define  MIN_I           0
+#      define  MAX_I           63
+#endif
+
+       if(ms_poll == MS_PTY || ms_poll == 0) {
+               int     i;
+
+               for(i = MIN_I; i <= MAX_I && master < 0; i++) {
+                       sprintf(master_name, MASTER_TMPL, i);
+                       if((master = open(master_name, O_RDWR)) >= 0) {
+                               sprintf(slave_name, SLAVE_TMPL, i);
+                               if((slave = open(slave_name, O_RDWR)) < 0)
+                                       close(master);
+                       }
+               }
+
+               if(master >= 0 && slave >= 0)
+                       ms_type = MS_PTY;
+       }
+
+#      undef   MASTER_TMPL
+#      undef   SLAVE_TMPL
+#      undef   MIN_I
+#      undef   MAX_I
+#endif
+
+
+       if(! ms_type)
+               return(-1);
+
+       VT_Line = strdup(strrchr(slave_name, '/') + 1);
+
+       switch((VT_ChildPID = fork())) {
+       case -1:
+               return(-1);
+               break;
+
+       case 0:
+#ifdef SCO
+               setsid();
+#endif
+               close(0);
+               close(1);
+               close(2);
+
+               i = open(slave_name, O_RDWR);
+               o = open(slave_name, O_RDWR);
+               e = open(slave_name, O_RDWR);
+
+#ifdef LINUX
+               setsid();
+               if (ioctl(slave, TIOCSCTTY, (char *)NULL) == -1)
+                       exit(1);
+#endif
+#ifdef SCO
+               tcsetpgrp(0, getpid());
+#endif
+
+               VT_Start_utmp();
+
+               system("stty sane");
+               execlp("/bin/login", "login", "-c", (char*)0);
+               exit(1);
+               break;
+
+       default:
+               VT_Mode = True;
+               VT_Status = VT_OPEN;
+               VT_ChildDied = False;
+               VT_Fd = master;
+
+               signal(SIGCLD, VT_SigCLD);
+
+               signal(SIGHUP, VT_SigEXIT);
+               signal(SIGTERM, VT_SigEXIT);
+               signal(SIGINT, VT_SigEXIT);
+               signal(SIGQUIT, VT_SigEXIT);
+
+               memset(OutBuf, 0, sizeof(OutBuf));
+               OutBuf [4] = 0x06;
+               _smb_setlen(OutBuf, 1);
+
+               send_smb(Client,OutBuf);
+
+               return(0);
+               break;
+       }
+}
+
+
+/*
+VT_Output: transport data from socket to pty
+*/
+int    VT_Output(Buffer)
+char   *Buffer;
+{
+       int             i, len, nb;
+
+
+       if(VT_Status != VT_OPEN)
+               return(-1);
+
+       len = smb_len(Buffer);
+
+       nb = write(VT_Fd, Buffer + 4, len);
+
+       return((nb == len) ? 0 : -1);
+}
+
+
+/*
+VT_Input: transport data from pty to socket
+*/
+int    VT_Input(Buffer, Size)
+char   *Buffer;
+int            Size;
+{
+       int             len;
+
+
+       if(VT_Status != VT_OPEN)
+               return(-1);
+
+       memset(Buffer, 0, Size);
+       len = read(VT_Fd, Buffer + 4, MIN(VT_MAXREAD, Size));
+
+       _smb_setlen(Buffer, len);
+
+       return(len + 4);
+}
+
+
+/*
+VT_Process: main loop while in vt-mode
+*/
+void VT_Process()
+{
+       static int      trans_num = 0;
+       extern int      Client;
+       int                     nread;
+
+
+       VT_Start();
+
+       atexit(VT_AtExit);
+
+       while (True) {
+               int32                   len;      
+               int                             msg_type;
+               int                             msg_flags;
+               int                             counter;
+               int                             last_keepalive=0;
+               struct fd_set   si;
+               struct timeval  to, *top;
+               int                             n, ret, t;
+
+
+               errno = 0;
+               t = SMBD_SELECT_LOOP*1000;
+
+
+               FD_ZERO(&si);
+               FD_SET(Client, &si);
+
+               FD_SET(VT_Fd, &si);
+
+               if(t >= 0) {
+                       to.tv_sec = t / 1000;
+                       to.tv_usec = t - (to.tv_sec * 1000);
+
+                       top = &to;
+               } else
+                       top = NULL;
+
+               if(VT_ChildDied)
+                       goto leave_VT_Process;
+
+               n = select(MAX(VT_Fd, Client) + 1, &si, NULL, NULL, top);
+
+               if(VT_ChildDied)
+                       goto leave_VT_Process;
+       
+               if(n == 0) {
+                       int i;
+                       time_t t;
+                       BOOL allidle = True;
+                       extern int keepalive;
+       
+                       counter += SMBD_SELECT_LOOP;
+
+                       t = time(NULL);
+       
+                       if (keepalive && (counter-last_keepalive)>keepalive) {
+                               if (!send_keepalive(Client))
+                                       goto leave_VT_Process;
+                               last_keepalive = counter;
+                       }
+               } else if(n > 0) {
+                       counter = 0;
+
+                       if(FD_ISSET(VT_Fd, &si)) {
+                               /* got input from vt */
+                               nread = VT_Input(OutBuffer, MIN(BUFFER_SIZE,lp_maxxmit()));
+
+                               if(nread > 0)
+                                       send_smb(Client,OutBuffer);
+                       }
+
+                       if(FD_ISSET(Client, &si)) {
+                               /* got input from socket */
+
+                               if(receive_smb(Client,InBuffer, 0)) {
+                                       msg_type = CVAL(InBuffer,0);
+                                       msg_flags = CVAL(InBuffer,1);
+       
+                                       len = smb_len(InBuffer);
+       
+                                       DEBUG(6,("got message type 0x%x of len 0x%x\n",msg_type,len));
+       
+                                       nread = len + 4;
+        
+                                       DEBUG(3,("%s Transaction %d of length %d\n",timestring(),trans_num,nread));
+       
+                                       if(msg_type == 0)
+                                               VT_Output(InBuffer);
+                                       else {
+                                               nread = construct_reply(InBuffer,OutBuffer,nread,MIN(BUFFER_SIZE,lp_maxxmit()));
+       
+                                               if(nread > 0) {
+                                                       if (nread != smb_len(OutBuffer) + 4) {
+                                                               DEBUG(0,("ERROR: Invalid message response size! %d %d\n",
+                                                                       nread,
+                                                                       smb_len(OutBuffer)));
+                                                       } else
+                                                               send_smb(Client,OutBuffer);
+                                               }
+                                       }
+                               } else
+                                       if(errno == EBADF)
+                                               goto leave_VT_Process;
+                       }
+               }
+
+               trans_num++;
+       }
+
+       leave_VT_Process:
+/*
+               if(VT_ChildPID > 0)
+                       kill(VT_ChildPID, SIGHUP);
+
+               VT_Stop_utmp(VT_Line);
+               return;
+*/
+               close_sockets();
+               exit(0);
+}
diff --git a/source/utils/nmblookup.c b/source/utils/nmblookup.c
new file mode 100644 (file)
index 0000000..aa43173
--- /dev/null
@@ -0,0 +1,217 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   NBT client - used to lookup netbios names
+   Copyright (C) Andrew Tridgell 1994-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.
+   
+*/
+
+#ifdef SYSLOG
+#undef SYSLOG
+#endif
+
+#include "includes.h"
+#include "nameserv.h"
+
+extern int DEBUGLEVEL;
+
+extern pstring scope;
+
+extern struct in_addr bcast_ip;
+extern pstring myhostname;
+
+static BOOL got_bcast = False;
+
+int ServerFD= -1;
+
+/****************************************************************************
+  open the socket communication
+  **************************************************************************/
+static BOOL open_sockets(void)
+{
+  struct hostent *hp;
+  /* get host info */
+  if ((hp = Get_Hostbyname(myhostname)) == 0) 
+    {
+      DEBUG(0,( "Get_Hostbyname: Unknown host. %s\n",myhostname));
+      return False;
+    }   
+
+  ServerFD = open_socket_in(SOCK_DGRAM, 0,3);
+
+  if (ServerFD == -1)
+    return(False);
+
+  set_socket_options(ServerFD,"SO_BROADCAST");
+
+  DEBUG(3, ("Socket opened.\n"));
+  return True;
+}
+
+
+/****************************************************************************
+  initialise connect, service and file structs
+****************************************************************************/
+static BOOL init_structs(void )
+{
+  struct in_addr myip;
+
+  if (!get_myname(myhostname,&myip))
+    return(False);
+
+  /* Read the broadcast address from the interface */
+  {
+    struct in_addr ip0,ip2;
+
+    ip0 = myip;
+
+    if (!got_bcast) {
+      get_broadcast(&ip0,&bcast_ip,&ip2);
+
+      DEBUG(2,("Using broadcast %s\n",inet_ntoa(bcast_ip)));
+    }
+  }
+
+  return True;
+}
+
+/****************************************************************************
+usage on the program
+****************************************************************************/
+static void usage(void)
+{
+  printf("Usage: nmblookup [-M] [-B bcast address] [-d debuglevel] name\n");
+  printf("Version %s\n",VERSION);
+  printf("\t-d debuglevel         set the debuglevel\n");
+  printf("\t-B broadcast address  the address to use for broadcasts\n");
+  printf("\t-M                    searches for a master browser\n");
+  printf("\t-S                    lookup node status as well\n");
+  printf("\n");
+}
+
+
+/****************************************************************************
+  main program
+****************************************************************************/
+int main(int argc,char *argv[])
+{
+  int opt;
+  unsigned int lookup_type = 0x20;
+  pstring lookup;
+  extern int optind;
+  extern char *optarg;
+  BOOL find_master=False;
+  BOOL find_status=False;
+  int i;
+  
+  DEBUGLEVEL = 1;
+  *lookup = 0;
+
+  TimeInit();
+
+  setup_logging(argv[0],True);
+
+  charset_initialise();
+
+  while ((opt = getopt(argc, argv, "p:d:B:i:SMh")) != EOF)
+    switch (opt)
+      {
+      case 'B':
+       {
+         unsigned long a = interpret_addr(optarg);
+         putip((char *)&bcast_ip,(char *)&a);
+         got_bcast = True;
+       }
+       break;
+      case 'i':
+       strcpy(scope,optarg);
+       strupper(scope);
+       break;
+      case 'M':
+       find_master = True;
+       break;
+      case 'S':
+       find_status = True;
+       break;
+      case 'd':
+       DEBUGLEVEL = atoi(optarg);
+       break;
+      case 'h':
+       usage();
+       exit(0);
+       break;
+      default:
+       usage();
+       exit(1);
+      }
+
+  if (argc < 2) {
+    usage();
+    exit(1);
+  }
+
+  init_structs();
+  if (!open_sockets()) return(1);
+
+  DEBUG(1,("Sending queries to %s\n",inet_ntoa(bcast_ip)));
+
+
+  for (i=optind;i<argc;i++)
+    {
+      BOOL bcast = True;
+      int retries = 2;
+      char *p;
+      struct in_addr ip;
+
+      strcpy(lookup,argv[i]);
+
+      if (find_master) {
+       if (*lookup == '-') {
+         strcpy(lookup,"\01\02__MSBROWSE__\02");
+         lookup_type = 1;
+       } else {
+         lookup_type = 0x1d;
+       }
+      }
+
+      p = strchr(lookup,'#');
+
+      if (p) {
+       *p = 0;
+       sscanf(p+1,"%x",&lookup_type);
+       bcast = False;
+       retries = 1;
+      }
+
+      if (name_query(ServerFD,lookup,lookup_type,bcast,True,
+                    bcast_ip,&ip,NULL)) 
+       {
+         printf("%s %s\n",inet_ntoa(ip),lookup);
+         if (find_status) 
+           {
+             printf("Looking up status of %s\n",inet_ntoa(ip));
+             name_status(ServerFD,lookup,lookup_type,True,ip,NULL,NULL,NULL);
+             printf("\n");
+           }
+      } else {
+       printf("couldn't find name %s\n",lookup);
+      }
+    }
+
+  return(0);
+}
diff --git a/source/utils/smbpasswd.c b/source/utils/smbpasswd.c
new file mode 100644 (file)
index 0000000..167eb2e
--- /dev/null
@@ -0,0 +1,456 @@
+#ifdef SMB_PASSWD
+
+/*
+ * Unix SMB/Netbios implementation. Version 1.9. smbpasswd module. Copyright
+ * (C) 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"
+#include "des.h"
+
+/* Static buffers we will return. */
+static struct smb_passwd pw_buf;
+static pstring  user_name;
+static unsigned char smbpwd[16];
+static unsigned char smbntpwd[16];
+
+static int gethexpwd(char *p, char *pwd)
+{
+       int i;
+       unsigned char   lonybble, hinybble;
+       char           *hexchars = "0123456789ABCDEF";
+       char           *p1, *p2;
+       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);
+}
+
+struct smb_passwd *
+_my_get_smbpwnam(FILE * fp, char *name, BOOL * valid_old_pwd, 
+               BOOL *got_valid_nt_entry, long *pwd_seekpos)
+{
+       char            linebuf[256];
+       unsigned char   c;
+       unsigned char  *p;
+       long            uidval;
+       long            linebuf_len;
+
+       /*
+        * Scan the file, a line at a time and check if the name matches.
+        */
+       while (!feof(fp)) {
+               linebuf[0] = '\0';
+               *pwd_seekpos = ftell(fp);
+
+               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.
+                */
+               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';
+
+               if ((linebuf[0] == 0) && feof(fp))
+                       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]:....ignored....
+                * 
+                * if Windows NT compatible passwords are also present.
+                */
+
+               if (linebuf[0] == '#' || linebuf[0] == '\0')
+                       continue;
+               p = (unsigned char *) strchr(linebuf, ':');
+               if (p == NULL)
+                       continue;
+               /*
+                * As 256 is shorter than a pstring we don't need to check
+                * length here - if this ever changes....
+                */
+               strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
+               user_name[PTR_DIFF(p, linebuf)] = '\0';
+               if (!strequal(user_name, name))
+                       continue;
+
+               /* User name matches - get uid and password */
+               p++;            /* Go past ':' */
+               if (!isdigit(*p))
+                       return (False);
+
+               uidval = atoi((char *) p);
+               while (*p && isdigit(*p))
+                       p++;
+
+               if (*p != ':')
+                       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++;
+               *pwd_seekpos += PTR_DIFF(p, linebuf);   /* Save exact position
+                                                        * of passwd in file -
+                                                        * this is used by
+                                                        * smbpasswd.c */
+               if (*p == '*' || *p == 'X') {
+                       /* Password deliberately invalid - end here. */
+                       *valid_old_pwd = False;
+                       *got_valid_nt_entry = False;
+                       pw_buf.smb_nt_passwd = NULL;    /* No NT password (yet)*/
+
+                       /* 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)) && (p[32] == ':')) {
+                               /* NT Entry was valid - even if 'X' or '*', can be overwritten */
+                               *got_valid_nt_entry = True;
+                               if (*p != '*' && *p != 'X') {
+                                       if(gethexpwd(p,smbntpwd))
+                                               pw_buf.smb_nt_passwd = smbntpwd;
+                               }
+                       }
+                       pw_buf.smb_name = user_name;
+                       pw_buf.smb_userid = uidval;
+                       pw_buf.smb_passwd = NULL;       /* No password */
+                       return (&pw_buf);
+               }
+               if (linebuf_len < (PTR_DIFF(p, linebuf) + 33))
+                       return (False);
+
+               if (p[32] != ':')
+                       return (False);
+
+               if (!strncasecmp(p, "NO PASSWORD", 11)) {
+                       pw_buf.smb_passwd = NULL;       /* No password */
+               } else {
+                       if(!gethexpwd(p,smbpwd))
+                               return False;
+                       pw_buf.smb_passwd = smbpwd;
+               }
+
+               pw_buf.smb_name = user_name;
+               pw_buf.smb_userid = uidval;
+               pw_buf.smb_nt_passwd = NULL;
+               *got_valid_nt_entry = False;
+               *valid_old_pwd = True;
+
+               /* 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)) && (p[32] == ':')) {
+                       /* NT Entry was valid - even if 'X' or '*', can be overwritten */
+                       *got_valid_nt_entry = True;
+                       if (*p != '*' && *p != 'X') {
+                               if(gethexpwd(p,smbntpwd))
+                                       pw_buf.smb_nt_passwd = smbntpwd;
+                       }
+               }
+               return &pw_buf;
+       }
+       return NULL;
+}
+
+/*
+ * Print command usage on stderr and die.
+ */
+void 
+usage(char *name)
+{
+       fprintf(stderr, "Usage is : %s [username]\n", name);
+       exit(1);
+}
+
+int main(int argc, char **argv)
+{
+  int             real_uid;
+  struct passwd  *pwd;
+  fstring         old_passwd;
+  uchar           old_p16[16];
+  uchar           old_nt_p16[16];
+  fstring         new_passwd;
+  uchar           new_p16[16];
+  uchar           new_nt_p16[16];
+  char           *p;
+  struct smb_passwd *smb_pwent;
+  FILE           *fp;
+  BOOL            valid_old_pwd = False;
+  BOOL                         got_valid_nt_entry = False;
+  long            seekpos;
+  int             pwfd;
+  char            ascii_p16[66];
+  char            c;
+  int             ret, i, err, writelen;
+  int             lockfd = -1;
+  char           *pfile = SMB_PASSWD_FILE;
+  char            readbuf[16 * 1024];
+  
+  setup_logging(argv[0],True);
+  
+  charset_initialise();
+  
+#ifndef DEBUG_PASSWORD
+  /* Check the effective uid */
+  if (geteuid() != 0) {
+    fprintf(stderr, "%s: Must be setuid root.\n", argv[0]);
+    exit(1);
+  }
+#endif
+  
+  /* Get the real uid */
+  real_uid = getuid();
+  
+  /* Deal with usage problems */
+  if (real_uid == 0) {
+    /* As root we can change anothers password. */
+    if (argc != 1 && argc != 2)
+      usage(argv[0]);
+  } else if (argc != 1)
+    usage(argv[0]);
+  
+  
+  if (real_uid == 0 && argc == 2) {
+    /* If we are root we can change anothers password. */
+    strncpy(user_name, argv[1], sizeof(user_name) - 1);
+    user_name[sizeof(user_name) - 1] = '\0';
+    pwd = getpwnam(user_name);
+  } else {
+    pwd = getpwuid(real_uid);
+  }
+  
+  if (pwd == 0) {
+    fprintf(stderr, "%s: Unable to get UNIX password entry for user.\n", argv[0]);
+    exit(1);
+  }
+  /* If we are root we don't ask for the old password. */
+  old_passwd[0] = '\0';
+  if (real_uid != 0) {
+    p = getpass("Old SMB password:");
+    strncpy(old_passwd, p, sizeof(fstring));
+    old_passwd[sizeof(fstring)-1] = '\0';
+  }
+  new_passwd[0] = '\0';
+  p = getpass("New SMB password:");
+  strncpy(new_passwd, p, sizeof(fstring));
+  new_passwd[sizeof(fstring)-1] = '\0';
+  p = getpass("Retype new SMB password:");
+  if (strcmp(p, new_passwd)) {
+    fprintf(stderr, "%s: Mismatch - password unchanged.\n", argv[0]);
+    exit(1);
+  }
+  
+  if (new_passwd[0] == '\0') {
+    printf("Password not set\n");
+    exit(0);
+  }
+  
+  /* Calculate the MD4 hash (NT compatible) of the old and new passwords */
+  memset(old_nt_p16, '\0', 16);
+  E_md4hash((uchar *)old_passwd, old_nt_p16);
+  
+  memset(new_nt_p16, '\0', 16);
+  E_md4hash((uchar *) new_passwd, new_nt_p16);
+  
+  /* Mangle the passwords into Lanman format */
+  old_passwd[14] = '\0';
+  strupper(old_passwd);
+  new_passwd[14] = '\0';
+  strupper(new_passwd);
+  
+  /*
+   * Calculate the SMB (lanman) hash functions of both old and new passwords.
+   */
+  
+  memset(old_p16, '\0', 16);
+  E_P16((uchar *) old_passwd, old_p16);
+  
+  memset(new_p16, '\0', 16);
+  E_P16((uchar *) new_passwd, new_p16);
+  
+  /*
+   * Open the smbpaswd file XXXX - we need to parse smb.conf to get the
+   * filename
+   */
+  if ((fp = fopen(pfile, "r+")) == NULL) {
+    err = errno;
+    fprintf(stderr, "%s: Failed to open password file %s.\n",
+           argv[0], pfile);
+    errno = err;
+    perror(argv[0]);
+    exit(err);
+  }
+  /* Set read buffer to 16k for effiecient reads */
+  setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
+  
+  /* make sure it is only rw by the owner */
+  chmod(pfile, 0600);
+  
+  /* Lock the smbpasswd file for write. */
+  if ((lockfd = pw_file_lock(pfile, F_WRLCK, 5)) < 0) {
+    err = errno;
+    fprintf(stderr, "%s: Failed to lock password file %s.\n",
+           argv[0], pfile);
+    fclose(fp);
+    errno = err;
+    perror(argv[0]);
+    exit(err);
+  }
+  /* Get the smb passwd entry for this user */
+  smb_pwent = _my_get_smbpwnam(fp, pwd->pw_name, &valid_old_pwd, 
+                              &got_valid_nt_entry, &seekpos);
+  if (smb_pwent == NULL) {
+    fprintf(stderr, "%s: Failed to find entry for user %s in file %s.\n",
+           argv[0], pwd->pw_name, pfile);
+    fclose(fp);
+    pw_file_unlock(lockfd);
+    exit(1);
+  }
+  /* If we are root we don't need to check the old password. */
+  if (real_uid != 0) {
+    if ((valid_old_pwd == False) || (smb_pwent->smb_passwd == NULL)) {
+      fprintf(stderr, "%s: User %s is disabled, plase contact your administrator to enable it.\n", argv[0], pwd->pw_name);
+      fclose(fp);
+      pw_file_unlock(lockfd);
+      exit(1);
+    }
+    /* Check the old Lanman password */
+    if (memcmp(old_p16, smb_pwent->smb_passwd, 16)) {
+      fprintf(stderr, "%s: Couldn't change password.\n", argv[0]);
+      fclose(fp);
+      pw_file_unlock(lockfd);
+      exit(1);
+    }
+    /* Check the NT password if it exists */
+    if (smb_pwent->smb_nt_passwd != NULL) {
+      if (memcmp(old_nt_p16, smb_pwent->smb_nt_passwd, 16)) {
+       fprintf(stderr, "%s: Couldn't change password.\n", argv[0]);
+       fclose(fp);
+       pw_file_unlock(lockfd);
+       exit(1);
+      }
+    }
+  }
+  /*
+   * If we get here either we were root or the old password checked out
+   * ok.
+   */
+  /* Create the 32 byte representation of the new p16 */
+  for (i = 0; i < 16; i++) {
+    sprintf(&ascii_p16[i * 2], "%02X", (uchar) new_p16[i]);
+  }
+  if(got_valid_nt_entry) {
+    /* Add on the NT md4 hash */
+    ascii_p16[32] = ':';
+    for (i = 0; i < 16; i++) {
+      sprintf(&ascii_p16[(i * 2)+33], "%02X", (uchar) new_nt_p16[i]);
+    }
+  }
+  /*
+   * Do an atomic write into the file at the position defined by
+   * seekpos.
+   */
+  pwfd = fileno(fp);
+  ret = lseek(pwfd, seekpos - 1, SEEK_SET);
+  if (ret != seekpos - 1) {
+    err = errno;
+    fprintf(stderr, "%s: seek fail on file %s.\n",
+           argv[0], pfile);
+    fclose(fp);
+    errno = err;
+    perror(argv[0]);
+    pw_file_unlock(lockfd);
+    exit(1);
+  }
+  /* Sanity check - ensure the character is a ':' */
+  if (read(pwfd, &c, 1) != 1) {
+    err = errno;
+    fprintf(stderr, "%s: read fail on file %s.\n",
+           argv[0], pfile);
+    fclose(fp);
+    errno = err;
+    perror(argv[0]);
+    pw_file_unlock(lockfd);
+    exit(1);
+  }
+  if (c != ':') {
+    fprintf(stderr, "%s: sanity check on passwd file %s failed.\n",
+           argv[0], pfile);
+    fclose(fp);
+    pw_file_unlock(lockfd);
+    exit(1);
+  }
+  writelen = (got_valid_nt_entry) ? 65 : 32;
+  if (write(pwfd, ascii_p16, writelen) != writelen) {
+    err = errno;
+    fprintf(stderr, "%s: write fail in file %s.\n",
+           argv[0], pfile);
+    fclose(fp);
+    errno = err;
+    perror(argv[0]);
+    pw_file_unlock(lockfd);
+    exit(err);
+  }
+  fclose(fp);
+  pw_file_unlock(lockfd);
+  printf("Password changed\n");
+  return 0;
+}
+
+#else
+
+#include "includes.h"
+
+int 
+main(int argc, char **argv)
+{
+  printf("smb password encryption not selected in Makefile\n");
+  return 0;
+}
+#endif
diff --git a/source/utils/status.c b/source/utils/status.c
new file mode 100644 (file)
index 0000000..ed0ae53
--- /dev/null
@@ -0,0 +1,258 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   status reporting
+   Copyright (C) Andrew Tridgell 1994-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.
+*/
+
+/*
+ * This program reports current SMB connections
+ */
+
+#ifdef SYSLOG
+#undef SYSLOG
+#endif
+
+#include "includes.h"
+#include "loadparm.h"
+
+struct connect_record crec;
+extern int DEBUGLEVEL;
+extern FILE *dbf;
+
+static pstring Ucrit_username = "";                   /* added by OH */
+int            Ucrit_pid[100];  /* Ugly !!! */        /* added by OH */
+int            Ucrit_MaxPid=0;                        /* added by OH */
+unsigned int   Ucrit_IsActive = 0;                    /* added by OH */
+void           Ucrit_addUsername(pstring username);   /* added by OH */
+unsigned int   Ucrit_checkUsername(pstring username); /* added by OH */
+void           Ucrit_addPid(int pid);                 /* added by OH */
+unsigned int   Ucrit_checkPid(int pid);               /* added by OH */
+
+int main(int argc, char *argv[])
+{
+  FILE *f;
+  pstring fname;
+  int uid, c, n;
+  static pstring servicesf = CONFIGFILE;
+  extern char *optarg;
+  int verbose = 0;
+  void *dir;
+  char *s;
+  BOOL firstopen=True;
+  BOOL processes_only=False;
+  int last_pid=0;
+
+  setup_logging(argv[0],True);
+
+  charset_initialise();
+
+  DEBUGLEVEL = 0;
+  dbf = fopen("/dev/null","w");
+
+  if (getuid() != geteuid()) {
+    printf("smbstatus should not be run setuid\n");
+    return(1);
+  }
+
+  while ((c = getopt(argc, argv, "pdsu:")) != EOF) {
+    switch (c) {
+    case 'd':
+      verbose = 1;
+      break;
+    case 'p':
+      processes_only = 1;
+      break;
+    case 's':
+      strcpy(servicesf, optarg);
+      break;
+    case 'u':                                       /* added by OH */
+      Ucrit_addUsername(optarg);                    /* added by OH */
+      break;
+    default:
+      fprintf(stderr, "Usage: %s [-d] [-p] [-s configfile] [-u username]\n", *argv); /* changed by OH */
+      return (-1);
+    }
+  }
+
+
+
+  if (!lp_load(servicesf,False)) {
+    fprintf(stderr, "Can't load %s - run testparm to debug it\n", servicesf);
+    return (-1);
+  }
+
+  if (verbose) {
+    printf("using configfile = %s\n", servicesf);
+    printf("lockdir = %s\n", *lp_lockdir() ? lp_lockdir() : "NULL");
+  }
+
+  strcpy(fname,lp_lockdir());
+  standard_sub_basic(fname);
+  trim_string(fname,"","/");
+  strcat(fname,"/STATUS..LCK");
+
+  f = fopen(fname,"r");
+  if (!f) {
+    printf("Couldn't open status file %s\n",fname);
+    if (!lp_status(-1))
+      printf("You need to have status=yes in your smb config file\n");
+    return(0);
+  }
+
+  uid = getuid();
+
+  if (!processes_only) {
+    printf("\nSamba version %s\n",VERSION);
+
+    printf("Service      uid      gid      pid     machine\n");
+    printf("----------------------------------------------\n");
+  }
+
+  while (!feof(f))
+    {
+      if (fread(&crec,sizeof(crec),1,f) != 1)
+       break;
+      if ( crec.magic == 0x280267 && process_exists(crec.pid) 
+           && Ucrit_checkUsername(uidtoname(crec.uid))                      /* added by OH */
+         )
+      {
+        Ucrit_addPid(crec.pid);                                             /* added by OH */
+       if (processes_only) {
+         if (last_pid != crec.pid)
+           printf("%d\n",crec.pid);
+         last_pid = crec.pid; /* XXXX we can still get repeats, have to
+                                 add a sort at some time */
+       }
+       else      
+         printf("%-10.10s   %-8s %-8s %5d   %-8s (%s) %s",
+                crec.name,uidtoname(crec.uid),gidtoname(crec.gid),crec.pid,
+                crec.machine,crec.addr,
+                asctime(LocalTime(&crec.start,GMT_TO_LOCAL)));
+      }
+    }
+  fclose(f);
+
+  if (processes_only) exit(0);
+
+  printf("\n");
+
+  dir = opendir(lp_lockdir());
+  if (!dir) return(0);
+  while ((s=readdirname(dir))) {
+    char buf[16];
+    int pid,mode;
+    time_t t;
+    int fd;
+    pstring lname;
+    int dev,inode;
+
+    if (sscanf(s,"share.%d.%d",&dev,&inode)!=2) continue;
+
+    strcpy(lname,lp_lockdir());
+    trim_string(lname,NULL,"/");
+    strcat(lname,"/");
+    strcat(lname,s);
+
+    fd = open(lname,O_RDONLY,0);
+    if (fd < 0) continue;
+    if (read(fd,buf,16) != 16) continue;
+    n = read(fd,fname,sizeof(fname));
+    fname[MAX(n,0)]=0;
+    close(fd);
+
+    t = IVAL(buf,0);
+    mode = IVAL(buf,4);
+    pid = IVAL(buf,8);
+
+    if ( !Ucrit_checkPid(pid) )             /* added by OH */
+        continue;
+
+    if (IVAL(buf,12) != LOCKING_VERSION || !process_exists(pid)) {
+      if (unlink(lname)==0)
+        printf("Deleted stale share file %s\n",s);
+      continue;
+    }
+
+    fname[sizeof(fname)-1] = 0;
+
+    if (firstopen) {
+      firstopen=False;
+      printf("Locked files:\n");
+      printf("Pid    DenyMode   R/W     Name\n");
+      printf("------------------------------\n");
+    }
+
+
+    printf("%-5d  ",pid);
+    switch ((mode>>4)&0xF)
+      {
+      case DENY_NONE: printf("DENY_NONE  "); break;
+      case DENY_ALL:  printf("DENY_ALL   "); break;
+      case DENY_DOS:  printf("DENY_DOS   "); break;
+      case DENY_READ: printf("DENY_READ  "); break;
+      case DENY_WRITE:printf("DENY_WRITE "); break;
+      }
+    switch (mode&0xF) 
+      {
+      case 0: printf("RDONLY "); break;
+      case 1: printf("WRONLY "); break;
+      case 2: printf("RDWR   "); break;
+      }
+    printf(" %s   %s",fname,asctime(LocalTime(&t,GMT_TO_LOCAL)));
+  }
+  closedir(dir);
+
+  if (firstopen)
+    printf("No locked files\n");
+
+  return (0);
+}
+
+/* added by OH */
+void Ucrit_addUsername(pstring username)
+{
+  strcpy(Ucrit_username, username);
+  if(strlen(Ucrit_username) > 0)
+    Ucrit_IsActive = 1;
+}
+
+unsigned int Ucrit_checkUsername(pstring username)
+{
+  if ( !Ucrit_IsActive) return 1;
+  if (strcmp(Ucrit_username,username) ==0) return 1;
+  return 0;
+}
+
+void Ucrit_addPid(int pid)
+{
+  int i;
+  if ( !Ucrit_IsActive) return;
+  for (i=0;i<Ucrit_MaxPid;i++)
+    if( pid == Ucrit_pid[i] ) return;
+  Ucrit_pid[Ucrit_MaxPid++] = pid;
+}
+
+unsigned int   Ucrit_checkPid(int pid)
+{
+  int i;
+  if ( !Ucrit_IsActive) return 1;
+  for (i=0;i<Ucrit_MaxPid;i++)
+    if( pid == Ucrit_pid[i] ) return 1;
+  return 0;
+}
+
diff --git a/source/utils/testparm.c b/source/utils/testparm.c
new file mode 100644 (file)
index 0000000..e1f070a
--- /dev/null
@@ -0,0 +1,113 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   Test validity of smb.conf
+   Copyright (C) Karl Auer 1993, 1994
+
+   Extensively modified by Andrew Tridgell, 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.
+*/
+
+/*
+ * 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"
+#include "smb.h"
+#include "params.h"
+#include "loadparm.h"
+
+/* these live in util.c */
+extern FILE *dbf;
+extern int DEBUGLEVEL;
+
+int main(int argc, char *argv[])
+{
+  pstring configfile;
+  int s;
+
+  setup_logging(argv[0],True);
+  
+  charset_initialise();
+
+  if (argc < 2)
+    strcpy(configfile,CONFIGFILE);
+  else
+    strcpy(configfile,argv[1]);
+
+  dbf = stdout;
+  DEBUGLEVEL = 2;
+
+  printf("Load smb config files from %s\n",configfile);
+
+  if (!lp_load(configfile,False))
+    {
+      printf("Error loading services.\n");
+      return(1);
+    }
+
+
+  printf("Loaded services file OK.\n");
+
+  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;
+      }
+
+  if (argc < 4)
+    {
+      printf("Press enter to see a dump of your service definitions\n");
+      fflush(stdout);
+      getc(stdin);
+      lp_dump();      
+    }
+  
+  if (argc == 4)
+    {
+      struct from_host f;
+      f.name = argv[2];
+      f.addr = argv[3];
+      
+      /* 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),&f))
+             {
+               printf("Allow connection from %s (%s) to %s\n",
+                      f.name,f.addr,lp_servicename(s));
+             }
+           else
+             {
+               printf("Deny connection from %s (%s) to %s\n",
+                      f.name,f.addr,lp_servicename(s));
+             }
+         }
+    }
+  return(0);
+}
+
+
diff --git a/source/utils/testprns.c b/source/utils/testprns.c
new file mode 100644 (file)
index 0000000..89c6158
--- /dev/null
@@ -0,0 +1,72 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   test printer setup
+   Copyright (C) Karl Auer 1993, 1994
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the 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"
+#include "smb.h"
+#include "pcap.h"
+
+/* these live in util.c */
+extern FILE *dbf;
+extern int DEBUGLEVEL;
+
+int main(int argc, char *argv[])
+{
+   char *pszTemp;
+
+   setup_logging(argv[0],True);
+
+   charset_initialise();
+
+   if (argc < 2 || argc > 3)
+      printf("Usage: testprns printername [printcapfile]\n");
+   else
+   {
+      dbf = fopen("test.log", "w");
+      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]);
+         fclose(dbf);
+      }
+   }
+   return (0);
+}
+