summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSamba Release Account <samba-bugs@samba.org>1996-05-04 07:50:46 +0000
committerSamba Release Account <samba-bugs@samba.org>1996-05-04 07:50:46 +0000
commit291551d80711daab7b7581720bcd9a08d6096517 (patch)
tree2bda16b586bd019b0a75e6188132ac81dbb9e3f1
downloadsamba-291551d80711daab7b7581720bcd9a08d6096517.tar.gz
samba-291551d80711daab7b7581720bcd9a08d6096517.tar.xz
samba-291551d80711daab7b7581720bcd9a08d6096517.zip
Initial version imported to CVS
-rw-r--r--COPYING339
-rw-r--r--README111
-rw-r--r--docs/THANKS119
-rw-r--r--docs/announce129
-rw-r--r--docs/history165
-rw-r--r--docs/htmldocs/wfw_slip.htm175
-rw-r--r--docs/manpages/nmbd.8491
-rw-r--r--docs/manpages/samba.7190
-rw-r--r--docs/manpages/smb.conf.52719
-rw-r--r--docs/manpages/smbclient.11133
-rw-r--r--docs/manpages/smbd.8407
-rw-r--r--docs/manpages/smbrun.170
-rw-r--r--docs/manpages/smbstatus.152
-rw-r--r--docs/manpages/smbtar.1167
-rw-r--r--docs/manpages/testparm.1104
-rw-r--r--docs/manpages/testprns.1107
-rw-r--r--docs/samba.lsm26
-rw-r--r--docs/textdocs/BROWSING.txt145
-rw-r--r--docs/textdocs/BUGS.txt123
-rw-r--r--docs/textdocs/DIAGNOSIS.txt237
-rw-r--r--docs/textdocs/DNIX.txt69
-rw-r--r--docs/textdocs/DOMAIN.txt68
-rw-r--r--docs/textdocs/ENCRYPTION.txt333
-rw-r--r--docs/textdocs/HINTS.txt202
-rw-r--r--docs/textdocs/INSTALL.sambatar27
-rw-r--r--docs/textdocs/PROJECTS96
-rw-r--r--docs/textdocs/Passwords.txt42
-rw-r--r--docs/textdocs/README.DCEDFS79
-rw-r--r--docs/textdocs/README.jis124
-rw-r--r--docs/textdocs/README.sambatar15
-rw-r--r--docs/textdocs/SCO.txt12
-rw-r--r--docs/textdocs/SMBTAR.notes40
-rw-r--r--docs/textdocs/Speed.txt272
-rw-r--r--docs/textdocs/Support.txt376
-rw-r--r--docs/textdocs/UNIX-SMB.txt220
-rw-r--r--docs/textdocs/WinNT.txt56
-rw-r--r--examples/README6
-rw-r--r--examples/dce-dfs/README4
-rw-r--r--examples/dce-dfs/smb.conf42
-rw-r--r--examples/misc/extra_smbstatus47
-rw-r--r--examples/misc/wall.perl45
-rwxr-xr-xexamples/printing/smbprint77
-rw-r--r--examples/printing/smbprint.sysv52
-rw-r--r--examples/simple/README2
-rw-r--r--examples/simple/smb.conf165
-rw-r--r--examples/tridge/README8
-rw-r--r--examples/tridge/smb.conf101
-rw-r--r--examples/tridge/smb.conf.WinNT14
-rw-r--r--examples/tridge/smb.conf.fjall21
-rw-r--r--examples/tridge/smb.conf.lapland14
-rw-r--r--examples/tridge/smb.conf.vittjokk14
-rw-r--r--source/change-log1872
-rw-r--r--source/client/client.c4534
-rw-r--r--source/client/clitar.c1713
-rw-r--r--source/include/byteorder.h80
-rw-r--r--source/include/charset.h61
-rw-r--r--source/include/clitar.h17
-rw-r--r--source/include/includes.h1154
-rw-r--r--source/include/kanji.h130
-rw-r--r--source/include/local.h167
-rw-r--r--source/include/nameserv.h184
-rw-r--r--source/include/smb.h1006
-rw-r--r--source/include/trans2.h241
-rw-r--r--source/include/version.h1
-rw-r--r--source/include/vt_mode.h48
-rw-r--r--source/lib/access.c389
-rw-r--r--source/lib/charcnv.c126
-rw-r--r--source/lib/charset.c111
-rw-r--r--source/lib/fault.c86
-rw-r--r--source/lib/getsmbpass.c166
-rw-r--r--source/lib/kanji.c895
-rw-r--r--source/lib/md4.c299
-rw-r--r--source/lib/system.c222
-rw-r--r--source/lib/ufc.c782
-rw-r--r--source/lib/username.c246
-rw-r--r--source/lib/util.c4510
-rw-r--r--source/libsmb/nmblib.c936
-rw-r--r--source/libsmb/smbencrypt.c202
-rw-r--r--source/locking/locking.c330
-rw-r--r--source/md4.h58
-rw-r--r--source/nameserv.c2318
-rw-r--r--source/nmbsync.c303
-rw-r--r--source/param/loadparm.c1891
-rw-r--r--source/param/params.c335
-rw-r--r--source/passdb/smbpass.c304
-rw-r--r--source/printing/pcap.c383
-rw-r--r--source/printing/printing.c859
-rw-r--r--source/script/addtosmbpass74
-rwxr-xr-xsource/script/installbin.sh42
-rwxr-xr-xsource/script/installman.sh35
-rwxr-xr-xsource/script/mksmbpasswd.sh6
-rwxr-xr-xsource/script/revert.sh15
-rw-r--r--source/script/smbtar141
-rwxr-xr-xsource/script/updatesmbpasswd.sh14
-rw-r--r--source/smbd/chgpasswd.c376
-rw-r--r--source/smbd/dir.c955
-rw-r--r--source/smbd/ipc.c2779
-rw-r--r--source/smbd/mangle.c610
-rw-r--r--source/smbd/message.c204
-rw-r--r--source/smbd/password.c1416
-rw-r--r--source/smbd/reply.c3210
-rw-r--r--source/smbd/server.c4300
-rw-r--r--source/smbd/smbrun.c96
-rw-r--r--source/smbd/trans2.c1646
-rw-r--r--source/smbd/vt_mode.c496
-rw-r--r--source/utils/nmblookup.c217
-rw-r--r--source/utils/smbpasswd.c456
-rw-r--r--source/utils/status.c258
-rw-r--r--source/utils/testparm.c113
-rw-r--r--source/utils/testprns.c72
110 files changed, 54142 insertions, 0 deletions
diff --git a/COPYING b/COPYING
new file mode 100644
index 00000000000..a43ea2126fb
--- /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.
+
+ 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.)
+
+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.
+
+ 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.
+
+ 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
+
+ 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
index 00000000000..b7ef5d55957
--- /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
index 00000000000..6405da3f9f4
--- /dev/null
+++ b/docs/THANKS
@@ -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
index 00000000000..f761320f43e
--- /dev/null
+++ b/docs/announce
@@ -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
index 00000000000..83761e23b86
--- /dev/null
+++ b/docs/history
@@ -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
index 00000000000..5b4a0a5e539
--- /dev/null
+++ b/docs/htmldocs/wfw_slip.htm
@@ -0,0 +1,175 @@
+<HTML>
+<HEAD>
+<TITLE>Peter Karrer Announces SLIP for WFW</TITLE>
+</HEAD>
+<BODY>
+<H1><I>Winserve</I></H1>
+<HR>
+<H2><I>Peter Karrer Announces SLIP for WFW</I></H2>
+[NEW 03-22-95)
+<HR>
+<B>Hello,</B>
+<P>
+I've discovered a way to run WfW's TCP/IP-32 over a SLIP packet driver. This
+allows WfW users to do Windows networking over dialup lines just like it is
+possible with NT and the Windows 95 beta!
+<P>
+For instance, you can mount Microsoft's FTP server as a network drive in File
+Manager or connect to an MS Mail post office over the Internet. Of course,
+the usual Internet stuff works as well. Another interesting site is
+WINSERVE.001; check out www.winserve.com.
+<HR>
+This method should work with any class 1 (Ethernet II) packet driver. However,
+I'm not in a position to try anything else than SLIPPER/CSLIPPER.
+<HR>
+<H3>Files you need:</H3>
+<B>WFWT32.EXE:</B> ftp://ftp.microsoft.com/bussys/msclient/wfw/wfwt32.exe
+<P>
+ Microsoft's free TCP/IP for WfW. It's a self-extracting archive which
+ should be executed in an empty directory.
+<P>
+<B>SLIPPER.EXE:</B> ftp://biocserver.bioc.cwru.edu/pub/dos/slipper/slippr15.zip
+<P>
+ Peter Tattam's SLIP packet driver. CSLIPPER.EXE is a variant which supports
+ VJ header compression.
+<P>
+<B>PDETHER.EXE:</B> ftp://sjf-lwp.idz.sjf.novell.com/odi/pdether/pde105.zip
+<P>
+ Don Provan's ODI-over-Packet Driver shim. This *must* be version 1.05 (or
+ above).
+<P>
+<B>LSL.COM:</B>
+<P>
+ Novell's LAN Support Layer. If you're an owner of Windows 3.10, you'll
+ have it on one of your install disks. Use "expand a:lsl.co_ lsl.com" to
+ expand it. Microsoft has stopped bundling LSL.COM with WfW 3.11, though.
+ The newest version of LSL.COM can be downloaded as part of
+ ftp://ftp.novell.com/pub/netware/nwos/dosclnt12/vlms/vlmup2.exe.
+ However, it's not clear if this one may be legally used outside Netware
+ environments.
+<P>
+<B>NET.CFG:</B>
+<P>
+ A configuration file for LSL and PDETHER. It should contain the following
+ text:
+<P>
+<PRE>
+Link Support
+ Buffers 8 1600
+Link Driver PDETHER
+ Int 60
+ Frame Ethernet_II
+ Protocol IP 800 Ethernet_II
+ Protocol ARP 806 Ethernet_II
+ Protocol RARP 8035 Ethernet_II
+</PRE>
+<P>
+<B>DISCOMX.COM:</B>
+<P>
+ A little hack of mine to disable the COM port used by the SLIP packet driver.
+ Usage is e.g. "discomx 2" to disable COM2. This should be run before
+ starting WfW, otherwise you'll get "device conflict" messages. Here it is:
+<P><PRE>
+begin 644 discomx.com
+F,=N)V8H.@`"P(+^!`/.N3XH="=MT!DN`XP/1XS')!R:)CP`$S2``
+`
+end
+ </PRE>
+ (Save this text to disk as <I>filename</I>, then run "uudecode <I>filename</I>".
+ uudecode can be found, for instance, at
+ ftp://ftp.switch.ch/mirror/simtel/msdos/starter/uudecode.com )
+<P>
+<B>LMHOSTS:</B>
+ <P>
+ An optional file which should be stored in your Windows subdirectory. It is
+ used to map NetBIOS computer names to IP addresses. Example:
+<P>
+<PRE>
+198.105.232.1 ftp #PRE # ftp.microsoft.com
+204.118.34.11 winserve.001 #PRE # Winserve
+</PRE>
+<HR>
+<H3>How to install it:</H3>
+<P>
+<UL>
+<LI>Put the files mentioned above into a directory, e.g. C:\SLIP.
+<P>
+<LI>Put the following lines into AUTOEXEC.BAT:
+<P><PRE>
+ cd \slip
+ slipper com1 vec=60 baud=57600 ether (may vary with your modem setup)
+ lsl
+ pdether
+ discomx 1 (must correspond to SLIPPER's COM port)
+</PRE>
+ (If you use another vec= setting, you must update that in NET.CFG as well.)
+ Use CSLIPPER instead of SLIPPER if your SLIP provider supports VJC.
+<P>
+<LI>Start WfW.
+<UL>
+<LI>Under Windows Setup, choose "Change Network Settings".
+<LI>Select "Install Microsoft Windows Network".
+<LI>In "Drivers...", choose "Add Adapter"
+ and install the "IPXODI Support driver (Ethernet) [ODI/NDIS3]".
+<LI>In "Add Protocols...", select "Unlisted or Updated Protocol". When asked for a
+ driver disk, enter the directory where you expanded WFWT32.EXE.
+<LI>Configure TCP/IP (IP address, enable LMHOSTS lookup, try 204.118.34.11 as primary
+ WINS server). Remove all other protocols (NetBEUI, IPX/SPX).
+</UL>
+<P>
+<LI>Windows will probably update the first lines of AUTOEXEC.BAT with
+<P>
+<PRE>
+ c:\windows\net start
+ c:\windows\odihlp.exe.
+</PRE>
+ The "odihlp" line must be moved behind the "pdether" line.
+<P>
+<LI>Windows will also update NET.CFG with some "Frame" lines. These must
+ be removed (except "Frame Ethernet_II").
+<P>
+<LI>Somehow, you will have to dial in to your SLIP provider. I do it manually
+ before slipper (or cslipper) gets loaded, using a DOS-based terminal program.
+ But there are some automatic dialers around. I've seen recommendations for
+ ftp://mvmpc9.ciw.uni-karlsruhe.de/x-slip/slip_it.exe.
+<P>
+<LI>To connect to Microsoft's FTP server (or Winserve) go into File Manager,
+ choose "Connect Network drive" and enter "\\ftp" or "\\winserve.001" into
+ the "Path:" field.
+</UL>
+<HR>
+<H3>How it works:</H3>
+<P>
+Microsoft's TCP/IP-32 requires an NDIS3 interface. NDIS is Microsoft's way
+to interface with a network.
+<P>
+WfW also contains an NDIS3-over-ODI "shim", whose real mode component is
+ODIHLP.EXE. ODI is Novell's way to interface with a network.
+<P>
+SLIPPER is a Packet Driver (PD) for use over serial lines. PDs are everybody
+else's way to interface with a network. SLIPPER's "ether" option makes it
+look like an Ethernet PD to applications using it.
+<P>
+A "shim" is a program which simulates a network application programming
+interface on top of another.
+<P>
+There is no NDIS SLIP driver which would work with WfW.
+<P>
+There is no NDIS-over-PD shim.
+<P>
+However, there's an ODI-over-PD shim (PDETHER) and an NDIS-over-ODI shim
+(ODIHLP etc.)
+<P>
+OK, so let's do NDIS-over-ODI-over-PD!
+ <P>
+This should have worked all the time; however, a non-feature in PDETHER
+versions < 1.05 has prevented the method from functioning until now.
+<HR>
+<B>Questions, suggestions etc. please to
+<P>
+<PRE>
+Peter Karrer pkarrer@ife.ee.ethz.ch
+</PRE>
+</B>
+</BODY>
+</HTML>
diff --git a/docs/manpages/nmbd.8 b/docs/manpages/nmbd.8
new file mode 100644
index 00000000000..e42f194cdee
--- /dev/null
+++ b/docs/manpages/nmbd.8
@@ -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
index 00000000000..0c81f736b6e
--- /dev/null
+++ b/docs/manpages/samba.7
@@ -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
index 00000000000..933d71ff0c3
--- /dev/null
+++ b/docs/manpages/smb.conf.5
@@ -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
index 00000000000..5590e01296e
--- /dev/null
+++ b/docs/manpages/smbclient.1
@@ -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
index 00000000000..bae41b2c479
--- /dev/null
+++ b/docs/manpages/smbd.8
@@ -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
index 00000000000..1608d3bb345
--- /dev/null
+++ b/docs/manpages/smbrun.1
@@ -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
index 00000000000..76dc50cbb53
--- /dev/null
+++ b/docs/manpages/smbstatus.1
@@ -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
index 00000000000..0f1c38c271f
--- /dev/null
+++ b/docs/manpages/smbtar.1
@@ -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
index 00000000000..4a0ffcbc489
--- /dev/null
+++ b/docs/manpages/testparm.1
@@ -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
index 00000000000..f1c3d3ef020
--- /dev/null
+++ b/docs/manpages/testprns.1
@@ -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
index 00000000000..503ba1ec94b
--- /dev/null
+++ b/docs/samba.lsm
@@ -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
index 00000000000..8a09d2274fb
--- /dev/null
+++ b/docs/textdocs/BROWSING.txt
@@ -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
index 00000000000..e0fd6951477
--- /dev/null
+++ b/docs/textdocs/BUGS.txt
@@ -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
index 00000000000..6681bdc4bcb
--- /dev/null
+++ b/docs/textdocs/DIAGNOSIS.txt
@@ -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
index 00000000000..51005e6ec8c
--- /dev/null
+++ b/docs/textdocs/DNIX.txt
@@ -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
index 00000000000..31e19675fae
--- /dev/null
+++ b/docs/textdocs/DOMAIN.txt
@@ -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
index 00000000000..046b473e9a1
--- /dev/null
+++ b/docs/textdocs/ENCRYPTION.txt
@@ -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
index 00000000000..953650bdd3e
--- /dev/null
+++ b/docs/textdocs/HINTS.txt
@@ -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
index 00000000000..388e2a3eb6f
--- /dev/null
+++ b/docs/textdocs/INSTALL.sambatar
@@ -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
index 00000000000..cf903f2c6dd
--- /dev/null
+++ b/docs/textdocs/PROJECTS
@@ -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
index 00000000000..e06876fecae
--- /dev/null
+++ b/docs/textdocs/Passwords.txt
@@ -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
index 00000000000..f84b84bb686
--- /dev/null
+++ b/docs/textdocs/README.DCEDFS
@@ -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
index 00000000000..2ac6716a6f6
--- /dev/null
+++ b/docs/textdocs/README.jis
@@ -0,0 +1,124 @@
+$B!|(B samba $BF|K\8lBP1~$K$D$$$F(B
+
+1. $BL\E*(B
+
+ $BF|K\8lBP1~$O!"(B
+
+ (1) MS-Windows $B>e$G!"4A;z%U%!%$%kL>$r$I$&$7$F$b07$&I,MW$N$"$k%"%W%j%1!<%7%g%s$,$A$c(B
+ $B$s$HF0:n$9$k!#Nc$($P!"(BMS-WORD 5 $B$J$I$O!"%$%s%9%H!<%k;~$K4A;z$N%U%!%$%kL>$r>!<j(B
+ $B$K$D$1$F$7$^$$$^$9!#$3$&$$$C$?>l9g$K$A$c$s$HBP1~$G$-$k$h$&$K$9$k!#(B
+
+ (2) UNIX $B$O!":G6a$G$O$[$H$s$I$N$b$N$,(B 8 bits $B$N%U%!%$%kL>$r%5%]!<%H$7$F$$$^$9$,!"(B
+ $BCf$K$O!"$3$l$r%5%]!<%H$7$F$$$J$$$b$N$b$"$j$^$9!#$3$N$h$&$J>l9g$G$b!"(B(1)$B$NL\E*(B
+ $B$,K~B-$G$-$k$h$&$K$9$k!#(B
+
+ $B$rL\E*$H$7$F$$$^$9!#$=$N$?$a!"F|K\8lBP1~$O!"I,MW:G>.8B$7$+9T$J$C$F$*$j$^$;$s!#(B
+
+2. $BMxMQJ}K!(B
+
+(1) $BDI2C$7$?%Q%i%a!<%?(B
+
+ smb.conf $B%U%!%$%k$N(B global $B%;%/%7%g%s$K0J2<$N%Q%i%a!<%?$r@_Dj$G$-$k$h$&$K$7$^$7$?!#(B
+
+ [global]
+ ....
+ coding system = <$B%3!<%I7O(B>
+
+ $B$3$3$G;XDj$5$l$?%3!<%I7O$,(B UNIX $B>e$N%U%!%$%k%7%9%F%`$N%U%!%$%kL>$N%3!<%I$K$J$j$^$9!#(B
+ $B@_Dj$G$-$k$b$N$O!"<!$N$h$&$K$J$C$F$$$^$9!#(B
+
+ sjis: SHIFT JIS (MS $B4A;z%3!<%I(B)
+ euc: EUC $B%3!<%I(B
+ hex: 7 bits $B$N(B ASCII $B%3!<%I0J30$N%3!<%I$r0J2<$N7A<0$GI=$9J}<0$G$9!#Nc$($P!"(B
+ '$B%*%U%#%9(B' $B$H$$$&L>A0$O!"(B':83:49:83:74:83:42:83:58' $B$N$h$&$K!"(B':' $B$N8e$K#27e(B
+ $B$N(B16$B?J?t$rB3$1$k7A<0$K$J$j$^$9!#(B
+ $B$3$3$G!"(B':' $B$rB>$NJ8;z$KJQ99$7$?$$>l9g$O!"(Bhex $B$N8e$m$K$=$NJ8;z$r;XDj$7$^$9!#(B
+ $BNc$($P!"(B@$B$rJQ$o$j$K;H$$$?$$>l9g$O!"(B'hex@'$B$N$h$&$K;XDj$7$^$9!#(B
+ JIS $B%3!<%I$K$D$$$F$O!"0J2<$NI=$r;2>H$7$F2<$5$$!#(B
+ $B(#(!(!(!(((!(!(!(!(((!(!(!(!(((!(!(!(!(((!(!(!(!(((!(!(!(!(((!(!(!(!(!(!(!(!(!($(B
+ $B(";XDj(B $B("4A;z3+;O("4A;z=*N;("%+%J3+;O("%+%J=*N;("1Q?t3+;O("Hw9M(B $B("(B
+ $B('(!(!(!(+(!(!(!(!(+(!(!(!(!(+(!(!(!(!(+(!(!(!(!(+(!(!(!(!(+(!(!(!(!(!(!(!(!(!()(B
+ $B("(Bjis7 $B("(B\E$B $B("(B\E(J $B("(B0x0e $B("(B0x0f $B("(B\E(J $B("(Bjis 7$BC10LId9f(B $B("(B
+ $B("(Bjunet $B("(B\E$B $B("(B\E(J $B("(B\E(I $B("(B\E(J $B("(B\E(J $B("(B7bits $B%3!<%I(B $B("(B
+ $B("(Bjis8 $B("(B\E$B $B("(B\E(J $B("(B-- $B("(B-- $B("(B\E(J $B("(Bjis 8$BC10LId9f(B $B("(B
+ $B("(Bj7bb $B("(B\E$B $B("(B\E(B $B("(B0x0e $B("(B0x0f $B("(B\E(B $B("(B $B("(B
+ $B("(Bj7bj $B("(B\E$B $B("(B\E(J $B("(B0x0e $B("(B0x0f $B("(B\E(J $B("(Bjis7$B$HF1$8(B $B("(B
+ $B("(Bj7bh $B("(B\E$B $B("(B\E(H $B("(B0x0e $B("(B0x0f $B("(B\E(H $B("(B $B("(B
+ $B("(Bj7@b $B("(B\E$@ $B("(B\E(B $B("(B0x0e $B("(B0x0f $B("(B\E(B $B("(B $B("(B
+ $B("(Bj7@j $B("(B\E$@ $B("(B\E(J $B("(B0x0e $B("(B0x0f $B("(B\E(J $B("(B $B("(B
+ $B("(Bj7@h $B("(B\E$@ $B("(B\E(H $B("(B0x0e $B("(B0x0f $B("(B\E(H $B("(B $B("(B
+ $B("(Bj8bb $B("(B\E$B $B("(B\E(B $B("(B-- $B("(B-- $B("(B\E(B $B("(B $B("(B
+ $B("(Bj8bj $B("(B\E$B $B("(B\E(J $B("(B-- $B("(B-- $B("(B\E(J $B("(Bjis8$B$HF1$8(B $B("(B
+ $B("(Bj8bh $B("(B\E$B $B("(B\E(H $B("(B-- $B("(B-- $B("(B\E(H $B("(B $B("(B
+ $B("(Bj8@b $B("(B\E@@ $B("(B\E(B $B("(B-- $B("(B-- $B("(B\E(B $B("(B $B("(B
+ $B("(Bj8@j $B("(B\E$@ $B("(B\E(J $B("(B-- $B("(B-- $B("(B\E(J $B("(B $B("(B
+ $B("(Bj8@h $B("(B\E$@ $B("(B\E(H $B("(B-- $B("(B-- $B("(B\E(H $B("(B $B("(B
+ $B("(Bjubb $B("(B\E$B $B("(B\E(B $B("(B\E(I $B("(B\E(B $B("(B\E(B $B("(B $B("(B
+ $B("(Bjubj $B("(B\E$B $B("(B\E(J $B("(B\E(I $B("(B\E(J $B("(B\E(J $B("(Bjunet$B$HF1$8(B $B("(B
+ $B("(Bjubh $B("(B\E$B $B("(B\E(H $B("(B\E(I $B("(B\E(H $B("(B\E(H $B("(B $B("(B
+ $B("(Bju@b $B("(B\E$@ $B("(B\E(B $B("(B\E(I $B("(B\E(B $B("(B\E(B $B("(B $B("(B
+ $B("(Bju@j $B("(B\E$@ $B("(B\E(J $B("(B\E(I $B("(B\E(J $B("(B\E(J $B("(B $B("(B
+ $B("(Bju@h $B("(B\E$@ $B("(B\E(H $B("(B\E(I $B("(B\E(H $B("(B\E(H $B("(B $B("(B
+ $B(&(!(!(!(*(!(!(!(!(*(!(!(!(!(*(!(!(!(!(*(!(!(!(!(*(!(!(!(!(*(!(!(!(!(!(!(!(!(!(%(B
+
+ $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<(B
+ $B$N$b$N$rG'<1$7$^$9!#(B
+ $B4A;z$N;O$^$j(B: \E$B $B$+(B \E$@
+ $B4A;z$N=*$j(B: \E(J $B$+(B \E(B $B$+(B \E(H
+
+(2) smbclient $B$N%*%W%7%g%s(B
+
+ $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(B
+ $B$rDI2C$7$^$7$?!#(B
+
+ -t <$B%?!<%_%J%k%3!<%I7O(B>
+
+ $B$3$3$G!"(B<$B%?!<%_%J%k%3!<%I7O(B>$B$K;XDj$G$-$k$b$N$O!">e$N(B<$B%3!<%I7O(B>$B$HF1$8$b$N$G$9!#(B
+
+(3) $B%G%U%)%k%H(B
+
+ $B%G%U%)%k%H$N%3!<%I7O$O!"%3%s%Q%$%k;~$K7h$^$j$^$9!#(B
+
+3. $B%3%s%Q%$%k;~$N@_Dj(B
+
+ Makefile $B$K@_Dj$9$k9`L\$r0J2<$K<($7$^$9!#(B
+
+(1) KANJI $B%U%i%0(B
+
+ $B%3%s%Q%$%k%*%W%7%g%s$K(B -DKANJI=\"$B%3!<%I7O(B\" $B$r;XDj$7$^$9!#$3$N%3!<%I7O$O(B 2. $B$G;X(B
+ $BDj$9$k$b$N$HF1$8$G$9!#Nc$($P!"(B-DKANJI=\"euc\" $B$r(BFLAGSM $B$K@_Dj$9$k$H(B UNIX $B>e$N%U%!(B
+ $B%$%kL>$O!"(BEUC $B%3!<%I$K$J$j$^$9!#$3$3$G;XDj$7$?%3!<%I7O$O!"%5!<%P5Z$S%/%i%$%"%s%H(B
+ $B%W%m%0%i%`$N%G%U%)%k%H$KCM$J$j$^$9!#(B
+
+3. $B@)8B;v9`(B
+
+(1) $B4A;z%3!<%I(B
+ smbd $B$rF0:n$5$;$k%[%9%H$N(B UNIX $B$,%5%]!<%H$7$F$$$J$$4A;z%3!<%I$O!"MxMQ$G$-$J$$$3$H$,(B
+ $B$"$j$^$9!#JQ$JF0:n$r$9$k$h$&$J$i(B hex $B$N;XDj$r$9$k$N$,NI$$$G$7$g$&!#(B
+
+(2) smbclient $B%3%^%s%I(B
+ $B%7%U%H%3!<%I$J$I$N4X78$G!"4A;z$d2>L>$r4^$s$@%U%!%$%kL>$N(B ls $B$NI=<($,Mp$l$k$3$H$,$"$j(B
+ $B$^$9!#(B
+
+(3) $B%o%$%k%I%+!<%I$K$D$$$F(B
+ $B$A$c$s$H$7$?%9%Z%C%/$,$h$/$o$+$i$J$+$C$?$N$G$9$,!"0l1~!"(BDOS/V $B$NF0:n$HF1$8F0:n$r9T$J(B
+ $B$&$h$&$K$J$C$F$$$^$9!#(B
+
+4. $B>c32Ey$N%l%]!<%H$K$D$$$F(B
+
+ $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(B
+$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(B
+$B$+!"$b$7$/$O%a!<%j%s%0%j%9%H$J$I$X%l%]!<%H$9$k$h$&$K$7$F2<$5$$!#(B
+
+5. $B$=$NB>(B
+
+ hex $B7A<0$NJQ49J}K!$O!"(B
+
+ $BBgLZ!wBgDM!&C^GH(B <ohki@gssm.otsuka.tsukuba.ac.jp>$B;a(B
+
+ $B$,:n$i$l$?%3!<%I$rMxMQ$7$F$$$^$9!#(B
+
+1994$BG/(B10$B7n(B28$BF|(B $BBh#1HG(B
+1995$BG/(B 8$B7n(B16$BF|(B $BBh#2HG(B
+$BF#ED(B $B?r(B fujita@ainix.isac.co.jp
+
diff --git a/docs/textdocs/README.sambatar b/docs/textdocs/README.sambatar
new file mode 100644
index 00000000000..26829952eb6
--- /dev/null
+++ b/docs/textdocs/README.sambatar
@@ -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
index 00000000000..1b3801471f7
--- /dev/null
+++ b/docs/textdocs/SCO.txt
@@ -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
index 00000000000..a23cbf2b325
--- /dev/null
+++ b/docs/textdocs/SMBTAR.notes
@@ -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
index 00000000000..5dfd70323b1
--- /dev/null
+++ b/docs/textdocs/Speed.txt
@@ -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
index 00000000000..d71bdaf7b3e
--- /dev/null
+++ b/docs/textdocs/Support.txt
@@ -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
index 00000000000..b2c064215cf
--- /dev/null
+++ b/docs/textdocs/UNIX-SMB.txt
@@ -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
index 00000000000..b57abb7742e
--- /dev/null
+++ b/docs/textdocs/WinNT.txt
@@ -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
index 00000000000..3e58e0faf1c
--- /dev/null
+++ b/examples/README
@@ -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
index 00000000000..4aaba8bb33e
--- /dev/null
+++ b/examples/dce-dfs/README
@@ -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
index 00000000000..f5f155b8e6c
--- /dev/null
+++ b/examples/dce-dfs/smb.conf
@@ -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
index 00000000000..b018f3dcce9
--- /dev/null
+++ b/examples/misc/extra_smbstatus
@@ -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
index 00000000000..fc3dc2e2c05
--- /dev/null
+++ b/examples/misc/wall.perl
@@ -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
index 00000000000..a80d60ce4fa
--- /dev/null
+++ b/examples/printing/smbprint
@@ -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
index 00000000000..3e1cec47f50
--- /dev/null
+++ b/examples/printing/smbprint.sysv
@@ -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
index 00000000000..9628aa8260d
--- /dev/null
+++ b/examples/simple/README
@@ -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
index 00000000000..cdf65b337f7
--- /dev/null
+++ b/examples/simple/smb.conf
@@ -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
index 00000000000..11c72f20b3a
--- /dev/null
+++ b/examples/tridge/README
@@ -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
index 00000000000..a2f269f3b76
--- /dev/null
+++ b/examples/tridge/smb.conf
@@ -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
index 00000000000..f490f830ca7
--- /dev/null
+++ b/examples/tridge/smb.conf.WinNT
@@ -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
index 00000000000..76f4d0e3cad
--- /dev/null
+++ b/examples/tridge/smb.conf.fjall
@@ -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
index 00000000000..f490f830ca7
--- /dev/null
+++ b/examples/tridge/smb.conf.lapland
@@ -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
index 00000000000..919ecd15420
--- /dev/null
+++ b/examples/tridge/smb.conf.vittjokk
@@ -0,0 +1,14 @@
+;protocol = LANMAN2
+log level = 2
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/source/change-log b/source/change-log
new file mode 100644
index 00000000000..e120ac6f02a
--- /dev/null
+++ b/source/change-log
@@ -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
index 00000000000..504cb5a0bb4
--- /dev/null
+++ b/source/client/client.c
@@ -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
index 00000000000..1433ec59412
--- /dev/null
+++ b/source/client/clitar.c
@@ -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
index 00000000000..899cd6c4991
--- /dev/null
+++ b/source/include/byteorder.h
@@ -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
index 00000000000..7091732223a
--- /dev/null
+++ b/source/include/charset.h
@@ -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
index 00000000000..2305fceeec7
--- /dev/null
+++ b/source/include/clitar.h
@@ -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
index 00000000000..cc2bbbfad7c
--- /dev/null
+++ b/source/include/includes.h
@@ -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
index 00000000000..4f18305c637
--- /dev/null
+++ b/source/include/kanji.h
@@ -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
index 00000000000..2775453e150
--- /dev/null
+++ b/source/include/local.h
@@ -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
index 00000000000..168dd4ba866
--- /dev/null
+++ b/source/include/nameserv.h
@@ -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
index 00000000000..b7faffa9e92
--- /dev/null
+++ b/source/include/smb.h
@@ -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
index 00000000000..cc366ccaea0
--- /dev/null
+++ b/source/include/trans2.h
@@ -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
index 00000000000..9ad8b7d44b5
--- /dev/null
+++ b/source/include/version.h
@@ -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
index 00000000000..85b481122ee
--- /dev/null
+++ b/source/include/vt_mode.h
@@ -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
index 00000000000..14a84b2fb44
--- /dev/null
+++ b/source/lib/access.c
@@ -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
index 00000000000..049390f2a43
--- /dev/null
+++ b/source/lib/charcnv.c
@@ -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
index 00000000000..ada3ef790aa
--- /dev/null
+++ b/source/lib/charset.c
@@ -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
index 00000000000..20c75f7876c
--- /dev/null
+++ b/source/lib/fault.c
@@ -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
index 00000000000..07a7dbfd9b5
--- /dev/null
+++ b/source/lib/getsmbpass.c
@@ -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
index 00000000000..0af476eb157
--- /dev/null
+++ b/source/lib/kanji.c
@@ -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
index 00000000000..485e231a784
--- /dev/null
+++ b/source/lib/md4.c
@@ -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
index 00000000000..938746e9c9d
--- /dev/null
+++ b/source/lib/system.c
@@ -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
index 00000000000..8417285821a
--- /dev/null
+++ b/source/lib/ufc.c
@@ -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
index 00000000000..3d214fbbdab
--- /dev/null
+++ b/source/lib/username.c
@@ -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
index 00000000000..7bd6298c4ca
--- /dev/null
+++ b/source/lib/util.c
@@ -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
index 00000000000..67432271737
--- /dev/null
+++ b/source/libsmb/nmblib.c
@@ -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
index 00000000000..a0683b5d282
--- /dev/null
+++ b/source/libsmb/smbencrypt.c
@@ -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
index 00000000000..6ff3ab5d125
--- /dev/null
+++ b/source/locking/locking.c
@@ -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
index 00000000000..3f60d75fe3c
--- /dev/null
+++ b/source/md4.h
@@ -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
index 00000000000..802b98ec0a0
--- /dev/null
+++ b/source/nameserv.c
@@ -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
index 00000000000..5a77d6cc486
--- /dev/null
+++ b/source/nmbsync.c
@@ -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
index 00000000000..c61ab26781f
--- /dev/null
+++ b/source/param/loadparm.c
@@ -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
index 00000000000..b9d61382a18
--- /dev/null
+++ b/source/param/params.c
@@ -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
index 00000000000..2dec15ffb43
--- /dev/null
+++ b/source/passdb/smbpass.c
@@ -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
index 00000000000..8973b1627fb
--- /dev/null
+++ b/source/printing/pcap.c
@@ -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
index 00000000000..1dd8921800a
--- /dev/null
+++ b/source/printing/printing.c
@@ -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
index 00000000000..42af518397c
--- /dev/null
+++ b/source/script/addtosmbpass
@@ -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
index 00000000000..633e6cb5bb2
--- /dev/null
+++ b/source/script/installbin.sh
@@ -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
index 00000000000..a79d157c5f5
--- /dev/null
+++ b/source/script/installman.sh
@@ -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
index 00000000000..6e592acd652
--- /dev/null
+++ b/source/script/mksmbpasswd.sh
@@ -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
index 00000000000..68b47bf39d0
--- /dev/null
+++ b/source/script/revert.sh
@@ -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
index 00000000000..fc032ed41cd
--- /dev/null
+++ b/source/script/smbtar
@@ -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
index 00000000000..1d7e0d7332f
--- /dev/null
+++ b/source/script/updatesmbpasswd.sh
@@ -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
index 00000000000..dc0514c1ed7
--- /dev/null
+++ b/source/smbd/chgpasswd.c
@@ -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
index 00000000000..ac6f918b9da
--- /dev/null
+++ b/source/smbd/dir.c
@@ -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
index 00000000000..8852e57e8b6
--- /dev/null
+++ b/source/smbd/ipc.c
@@ -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
index 00000000000..8f1490c528d
--- /dev/null
+++ b/source/smbd/mangle.c
@@ -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
index 00000000000..6a96b4c7a9c
--- /dev/null
+++ b/source/smbd/message.c
@@ -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
index 00000000000..87c1fef94c5
--- /dev/null
+++ b/source/smbd/password.c
@@ -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
index 00000000000..b7b51775bb8
--- /dev/null
+++ b/source/smbd/reply.c
@@ -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
index 00000000000..5d8facef33f
--- /dev/null
+++ b/source/smbd/server.c
@@ -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
index 00000000000..df12ae1f85c
--- /dev/null
+++ b/source/smbd/smbrun.c
@@ -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
index 00000000000..9d02123cf87
--- /dev/null
+++ b/source/smbd/trans2.c
@@ -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
index 00000000000..83b62a38ac2
--- /dev/null
+++ b/source/smbd/vt_mode.c
@@ -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
index 00000000000..aa431733322
--- /dev/null
+++ b/source/utils/nmblookup.c
@@ -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
index 00000000000..167eb2ed5f3
--- /dev/null
+++ b/source/utils/smbpasswd.c
@@ -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
index 00000000000..ed0ae532114
--- /dev/null
+++ b/source/utils/status.c
@@ -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
index 00000000000..e1f070a4b83
--- /dev/null
+++ b/source/utils/testparm.c
@@ -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
index 00000000000..89c615898d7
--- /dev/null
+++ b/source/utils/testprns.c
@@ -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);
+}
+