summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--COPYING339
-rw-r--r--README116
-rw-r--r--docs/MIRRORS23
-rw-r--r--docs/README.OS22
-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.8487
-rw-r--r--docs/manpages/samba.7190
-rw-r--r--docs/manpages/smb.conf.52762
-rw-r--r--docs/manpages/smbclient.11162
-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.faq254
-rw-r--r--docs/samba.lsm26
-rw-r--r--docs/textdocs/BROWSING.txt145
-rw-r--r--docs/textdocs/BUGS.txt120
-rw-r--r--docs/textdocs/DIAGNOSIS.txt243
-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.txt395
-rw-r--r--docs/textdocs/UNIX-SMB.txt223
-rw-r--r--docs/textdocs/WinNT.txt56
-rw-r--r--docs/textdocs/security_level.txt78
-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_smbstatus50
-rw-r--r--examples/misc/wall.perl69
-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/thoralf/smb.conf152
-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/.cvsignore10
-rw-r--r--source/change-log1875
-rw-r--r--source/client/client.c4608
-rw-r--r--source/client/clientutil.c1029
-rw-r--r--source/client/clitar.c1707
-rw-r--r--source/include/byteorder.h80
-rw-r--r--source/include/charset.h65
-rw-r--r--source/include/clitar.h17
-rw-r--r--source/include/includes.h1164
-rw-r--r--source/include/kanji.h130
-rw-r--r--source/include/local.h164
-rw-r--r--source/include/nameserv.h273
-rw-r--r--source/include/proto.h512
-rw-r--r--source/include/smb.h790
-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.c123
-rw-r--r--source/lib/charset.c111
-rw-r--r--source/lib/fault.c86
-rw-r--r--source/lib/getsmbpass.c165
-rw-r--r--source/lib/kanji.c894
-rw-r--r--source/lib/md4.c299
-rw-r--r--source/lib/system.c222
-rw-r--r--source/lib/time.c495
-rw-r--r--source/lib/ufc.c782
-rw-r--r--source/lib/username.c246
-rw-r--r--source/lib/util.c4056
-rw-r--r--source/libsmb/namequery.c292
-rw-r--r--source/libsmb/nmblib.c697
-rw-r--r--source/libsmb/smbencrypt.c189
-rw-r--r--source/loadparm.h5
-rw-r--r--source/localnet.h6
-rw-r--r--source/locking/locking.c330
-rw-r--r--source/md4.h58
-rw-r--r--source/nameannounce.c445
-rw-r--r--source/namedb.c709
-rw-r--r--source/nameelect.c369
-rw-r--r--source/nameresp.c628
-rw-r--r--source/nameserv.c1045
-rw-r--r--source/namework.c1017
-rw-r--r--source/nmbd/nmbd.c610
-rw-r--r--source/nmbsync.c183
-rw-r--r--source/param/loadparm.c1911
-rw-r--r--source/param/params.c327
-rw-r--r--source/params.h5
-rw-r--r--source/passdb/smbpass.c303
-rw-r--r--source/printing/pcap.c383
-rw-r--r--source/printing/printing.c936
-rw-r--r--source/script/addtosmbpass74
-rwxr-xr-xsource/script/installbin.sh42
-rwxr-xr-xsource/script/installman.sh35
-rwxr-xr-xsource/script/installscripts.sh26
-rw-r--r--source/script/mkproto.awk39
-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.c978
-rw-r--r--source/smbd/ipc.c2831
-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/quotas.c363
-rw-r--r--source/smbd/reply.c3214
-rw-r--r--source/smbd/server.c3770
-rw-r--r--source/smbd/smbrun.c97
-rw-r--r--source/smbd/trans2.c1646
-rw-r--r--source/smbd/uid.c360
-rw-r--r--source/smbd/vt_mode.c490
-rw-r--r--source/sockspy.c302
-rw-r--r--source/utils/nmblookup.c219
-rw-r--r--source/utils/smbpasswd.c457
-rw-r--r--source/utils/status.c263
-rw-r--r--source/utils/testparm.c115
-rw-r--r--source/utils/testprns.c74
134 files changed, 59152 insertions, 341 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..84bc00b2ef1
--- /dev/null
+++ b/README
@@ -0,0 +1,116 @@
+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, see docs/BUGS.txt for more details) 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@samba.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
+samba.anu.edu.au in the directory pub/samba/.
+
+MAILING LIST
+------------
+
+There is a mailing list for discussion of Samba. To subscribe send
+mail to listproc@samba.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@samba.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://samba.canberra.edu.au/pub/samba/
+
+As well as general information and documentation, this also has searchable
+archives of the mailing list and a user survey that shows who else is using
+this package. Have you registered with the survey yet? :-)
+
+It is maintained by Paul Blackman (thanks Paul!). You can contact him
+at ictinus@lake.canberra.edu.au.
+
+
+
diff --git a/docs/MIRRORS b/docs/MIRRORS
index 9fa10c9fc85..2feb9ba90e8 100644
--- a/docs/MIRRORS
+++ b/docs/MIRRORS
@@ -1,17 +1,18 @@
-The main Samba site is nimbus.anu.edu.au in pub/tridge/samba/. Contact
-samba-bugs@anu.edu.au for help with this site.
+The main Samba site is samba.anu.edu.au in pub/samba/. Contact
+samba-bugs@samba.anu.edu.au for help with this site.
Mirror sites include:
-
-ftp://src.doc.ic.ac.uk/packages/samba
-ftp://ftp.warwick.ac.uk/pub/linux/sunsite.unc-mirror/system/Network/samba
+ftp://nimbus.anu.edu.au/pub/tridge/samba
+ftp://sunsite.auc.dk/pub/unix/networking/samba/
+ftp://src.doc.ic.ac.uk/packages/samba/
+ftp://ftp.warwick.ac.uk/pub/linux/sunsite.unc-mirror/system/Network/samba/
ftp://sunsite.unc.edu/pub/Linux/system/Network/samba/
-ftp://ftp.choc.apana.org.au/pub/samba
+ftp://ftp.choc.apana.org.au/pub/samba/
ftp://ftp.uni-trier.de/pub/unix/network/samba/
-ftp://ftp.spectrum.titan.com/pub/samba
-ftp://ftp.demon.co.uk/pub/unix/unix/samba
-ftp://sunsite.mff.cuni.cz/Net/Protocols/Samba
+ftp://ftp.spectrum.titan.com/pub/samba/
+ftp://ftp.demon.co.uk/pub/unix/unix/samba/
+ftp://sunsite.mff.cuni.cz/Net/Protocols/Samba/
There are several others. Give archie a try.
@@ -21,8 +22,10 @@ ftp://ftp.markv.com/pub/samba (built by lance@fox.com)
AIX and DEC OSF/1 binaries are available from:
ftp://151.99.220.5/pub/samba (built by davide.migliavacca@inferentia.inet.it)
+QNX binaries and source code:
+ftp://quics.qnx.com/usr/free/staging/samba
Http sites include:
-http://lake.canberra.edu.au/pub/samba
+http://samba.canberra.edu.au/pub/samba
http://www.choc.apana.org.au/pub/samba
diff --git a/docs/README.OS2 b/docs/README.OS2
index ffd818d4ce8..a464dd9946c 100644
--- a/docs/README.OS2
+++ b/docs/README.OS2
@@ -41,7 +41,7 @@ get lpr working properly.
Source is available from the Samba WWW site :
-http://lake.canberra.edu.au/pub/samba/
+http://samba.canberra.edu.au/pub/samba/
EMX 0.9b is available from hobbes.nmsu.edu, ftp.cdrom.com, ftp.leo.org and
other OS/2 FTP sites. emxrt.zip contains the required libraries.
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..78775aa5ca3
--- /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 samba.anu.edu.au in
+the directory pub/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://samba.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..72e45b9d54b
--- /dev/null
+++ b/docs/manpages/nmbd.8
@@ -0,0 +1,487 @@
+.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
+
+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..bea20555e73
--- /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 samba.anu.edu.au in the directory pub/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://samba.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..7149be6a473
--- /dev/null
+++ b/docs/manpages/smb.conf.5
@@ -0,0 +1,2762 @@
+.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
+
+delete readonly
+
+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 delete readonly (S)
+This parameter allows readonly files to be deleted. This is not normal DOS
+semantics, but is allowed by Unix.
+
+This option may be useful for running applications such as rcs, where unix
+file ownership prevents changing file permissions, and dos semantics prevent
+deletion of a read only file.
+
+.B Default:
+ delete readonly = No
+
+.B Example:
+ delete readonly = Yes
+.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 six styles of printer status information are supported; BSD,
+SYSV, AIX, HPUX, QNX and PLP. 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 six styles of printer control are supported; BSD, SYSV, AIX
+HPUX, QNX and PLP. 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 six printing styles are supported. They are "printing =
+bsd", "printing = sysv", "printing = hpux", "printing = aix",
+"printing = qnx" and "printing = plp".
+
+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 wins proxy (G)
+
+This is a boolean that controls if nmbd will respond to broadcast name
+queries on behalf of other hosts. You may need to set this to no for
+some older clients.
+
+.B Default:
+ wins proxy = no
+.SS wins support (G)
+
+This boolean controls if Samba will act as a WINS server. You should
+normally set this to true unless you already have another WINS server
+on the network.
+
+.B Default:
+ wins support = yes
+.SS wins server (G)
+
+This specifies the DNS name of the WINS server that Samba should
+register with. If you have a WINS server on your network then you
+should set this to the WINS servers name.
+
+This option only takes effect if Samba is not acting as a WINS server
+itself.
+
+.B Default:
+ wins server =
+.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..e0af67ca1a4
--- /dev/null
+++ b/docs/manpages/smbclient.1
@@ -0,0 +1,1162 @@
+.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 -W
+.I workgroup
+] [
+.B -O
+.I socket options
+] [
+.B -p
+.I port number
+] [
+.B -c
+.I command string
+] [
+.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 -W
+.I workgroup
+
+.RS 3
+Override what workgroup is used for the connection. This may be needed
+to connect to some servers.
+.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
+
+.B -c
+.I command string
+
+.RS 3
+
+command string is a semicolon separated list of commands to be
+executed instead of prompting from stdin. -N is implied by -c.
+
+This is particularly useful in scripts and for printing stdin to
+the server, e.g. -c 'print -'.
+
+.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.faq b/docs/samba.faq
index 26570b7577a..86dd48877f8 100644
--- a/docs/samba.faq
+++ b/docs/samba.faq
@@ -5,7 +5,7 @@
SAMBA Suite
- (FAQ version 1.9.02, Samba version 1.09.02)
+ (FAQ version 1.9.15a, Samba version 1.09.15)
-------------------------------------------------------------------------------
@@ -54,18 +54,26 @@ SECTION ONE: General information
* 1: What is Samba?
Samba is a suite of programs which work together to allow clients to access
-Unix filespace and printers via the SMB (Session Message Block) protocol.
+to a server's filespace and printers via the SMB (Session Message Block)
+protocol. Initially written for Unix, Samba now also runs on Netware, OS/2 and
+AmigaDOS.
In practice, this means that you can redirect disks and printers to Unix disks
and printers from Lan Manager clients, Windows for Workgroups 3.11 clients,
-Windows NT clients and OS/2 clients. There is also a Unix client program
-supplied as part of the suite which allows Unix users to use an ftp-like
-interface to access filespace and printers on any other SMB servers.
+Windows NT clients, Linux clients and OS/2 clients. There is also a generic
+Unix client program supplied as part of the suite which allows Unix users to
+use an ftp-like interface to access filespace and printers on any other SMB
+servers. This gives the capability for these operating systems to behave much
+like a LAN Server or Windows NT Server machine, only with added functionality
+and flexibility designed to make life easier for administrators.
The components of the suite are (in summary):
- * smbd, the SMB server. This handles actual connections from clients
- * nmbd, the Netbios name server, which helps clients locate servers
+ * smbd, the SMB server. This handles actual connections from clients,
+ doing all the file, permission and username work
+ * nmbd, the Netbios name server, which helps clients locate servers,
+ doing the browsing work and managing domains as this capability is
+ being built into Samba
* smbclient, the Unix-hosted client program
* smbrun, a little 'glue' program to help the server run external
programs
@@ -75,32 +83,34 @@ The components of the suite are (in summary):
* smb.conf, the Samba configuration file
* smbprint, a sample script to allow a Unix host to use smbclient to
print to an SMB server
+ * documentation! DON'T neglect to read it - you will save a great deal
+ of time!
The suite is supplied with full source (of course!) and is GPLed.
The primary creator of the Samba suite is Andrew Tridgell. Later versions
incorporate much effort by many net.helpers. The man pages and this FAQ were
-written by Karl Auer.
+originally written by Karl Auer.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* 2: What is the current version of Samba?
-At time of writing, the current version was 1.9.12. If you want to be sure
+At time of writing, the current version was 1.9.15. If you want to be sure
check the bottom of the change-log file.
-(nimbus.anu.edu.au/pub/tridge/samba/change-log)
+(ftp://samba.anu.edu.au/pub/samba/alpha/change-log)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* 3: Where can I get it?
-The Samba suite is available via anonymous ftp from nimbus.anu.edu.au. The
+The Samba suite is available via anonymous ftp from samba.anu.edu.au. The
latest and greatest versions of the suite are in the directory:
-/pub/tridge/samba/
+/pub/samba/
Development (read "alpha") versions, which are NOT necessarily stable and which
do NOT necessarily have accurate documentation, are available in the directory:
-/pub/tridge/samba/alpha
+/pub/samba/alpha
Note that binaries are NOT included in any of the above. Samba is distributed
ONLY in source form, though binaries may be available from other sites. Recent
@@ -154,7 +164,10 @@ At time of writing, the Makefile claimed support for:
There are two mailing lists devoted to discussion of Samba-related matters.
There is also the newsgroup, comp.protocols.smb, which has a great deal of
-discussion on Samba.
+discussion on Samba. There is also a WWW site 'SAMBA Web Pages' at
+http://samba.canberra.edu.au/pub/samba/samba.html, under which there is a
+comprehensive survey of Samba users. Another useful resource is the hypertext
+archive of the Samba mailing list.
Send email to listproc@anu.edu.au. Make sure the subject line is blank, and
include the following two lines in the body of the message:
@@ -177,6 +190,9 @@ following two lines in the body of the message:
unsubscribe samba
unsubscribe samba-announce
+The From: line in your message MUST be the same address you used when you
+subscribed.
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* 6: Something's gone wrong - what should I do?
@@ -184,7 +200,9 @@ following two lines in the body of the message:
DO NOT post messages on mailing lists or in newsgroups until you have carried
out the first three steps given here!
-Firstly, see if there are any likely looking entries in this FAQ!
+Firstly, see if there are any likely looking entries in this FAQ! If you have
+just installed Samba, have you run through the checklist in DIAGNOSIS.txt? It
+can save you a lot of time and effort.
Secondly, read the man pages for smbd, nmbd and smb.conf, looking for topics
that relate to what you are trying to do.
@@ -195,10 +213,20 @@ problems. You may need to reconfigure the servers to provide more extensive
debugging information - usually level 2 or level 3 provide ample debugging
info. Inspect these logs closely, looking particularly for the string "Error:".
-If you successfully solve a problem, please mail me a succinct description of
-the symptom, the problem and the solution, so I can incorporate it in the next
-version of the FAQ.
+Fourthly, if you still haven't got anywhere, ask the mailing list or newsgroup.
+In general nobody minds answering questions provided you have followed the
+preceding steps. It might be a good idea to scan the archives of the mailing
+list, which are available through the Samba web site described in the previous
+section.
+
+If you successfully solve a problem, please mail the FAQ maintainer a succinct
+description of the symptom, the problem and the solution, so I can incorporate
+it in the next version.
+If you make changes to the source code, _please_ submit these patches so that
+everyone else gets the benefit of your work. This is one of the most important
+aspects to the maintainence of Samba. Send all patches to
+samba-bugs@samba.anu.edu.au, not Andrew Tridgell or any other individual.
===============================================================================
SECTION TWO: Compiling and installing Samba on a Unix host
@@ -211,7 +239,7 @@ SECTION THREE: Common client problems
* 1: I can't see the Samba server in any browse lists!
*** Until the FAQ can be updated, please check the file:
-*** ftp://nimbus.anu.adu.au/pub/tridge/samba/BROWSING.txt
+*** ftp://samba.anu.edu.au/pub/samba/BROWSING.txt
*** for more information on browsing.
If your GUI client does not permit you to select non-browsable servers, you may
@@ -225,7 +253,7 @@ client - check your client's documentation.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* 2: Some files that I KNOW are on the server doesn't show up when I view the
-directories from my client!
+ directories from my client!
If you check what files are not showing up, you will note that they are files
which contain upper case letters or which are otherwise not DOS-compatible (ie,
@@ -303,7 +331,9 @@ Nothing is wrong - Samba does not implement the primary domain name controller
stuff for several reasons, including the fact that the whole concept of a
primary domain controller and "logging in to a network" doesn't fit well with
clients possibly running on multiuser machines (such as users of smbclient
-under Unix).
+under Unix). Having said that, several developers are working hard on
+building it in to the next major version of Samba. If you can contribute,
+send a message to samba-bugs!
Seeing this message should not affect your ability to mount redirected disks
and printers, which is really what all this is about.
@@ -351,6 +381,58 @@ In earlier Samba versions there were some difficulties with the very latest
Microsoft products, particularly Excel 5 and Word for Windows 6. These should
have all been solved. If not then please let Andrew Tridgell know.
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+* 9: My "server string" doesn't seem to be recognized, my client reports the
+ default setting, eg. "Samba 1.9.15p4", instead of what I have changed it
+ to in the smb.conf file.
+
+You need to use the -C option in nmbd. The "server string" affects
+what smbd puts out and -C affects what nmbd puts out. In a future
+version these will probably be combined and -C will be removed, but
+for now use -C
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+* 10: When I attempt to get a listing of available resources from the Samba
+ server, my client reports
+ "This server is not configured to list shared resources".
+
+Your guest account is probably invalid for some reason. Samba uses
+the guest account for browsing in smbd. Check that your guest account is
+valid.
+
+See also 'guest account' in smb.conf man page.
+
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+* 11: You get the message "you appear to have a trapdoor uid system"
+ in your logs
+
+This can have several causes. It might be because you are using a uid
+or gid of 65535 or -1. This is a VERY bad idea, and is a big security
+hole. Check carefully in your /etc/passwd file and make sure that no
+user has uid 65535 or -1. Especially check the "nobody" user, as many
+broken systems are shipped with nobody setup with a uid of 65535.
+
+It might also mean that your OS has a trapdoor uid/gid system :-)
+
+This means that once a process changes effective uid from root to
+another user it can't go back to root. Unfortunately Samba relies on
+being able to change effective uid from root to non-root and back
+again to implement its security policy. If your OS has a trapdoor uid
+system this won't work, and several things in Samba may break. Less
+things will break if you use user or server level security instead of
+the default share level security, but you may still strike
+problems.
+
+The problems don't give rise to any security holes, so don't panic,
+but it does mean some of Samba's capabilities will be unavailable.
+In particular you will not be able to connect to the Samba server as
+two different uids at once. This may happen if you try to print as a
+"guest" while accessing a share as a normal user. It may also affect
+your ability to list the available shares as this is normally done as
+the guest user.
+
+Complain to your OS vendor and ask them to fix their system.
===============================================================================
SECTION FOUR: Specific client problems
@@ -381,11 +463,139 @@ but we're not done yet.
Rob
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+* 2: I am getting a "Session request failed (131,130)" error when I try to
+ connect to my Win95 PC with smbclient. I am able to connect from the PC
+ to the Samba server without problems. What gives?
+
+The following answer is provided by John E. Miller:
+
+I'll assume that you're able to ping back and forth between the machines by
+IP address and name, and that you're using some security model where you're
+confident that you've got user IDs and passwords right. The logging options
+(-d3 or greater) can help a lot with that. DNS and WINS configuration can
+also impact connectivity as well.
+
+Now, on to 'scope id's. Somewhere in your Win95 TCP/IP network configuration
+(I'm too much of an NT bigot to know where it's located in the Win95 setup,
+but I'll have to learn someday since I teach for a Microsoft Solution Provider
+Authorized Tech Education Center - what an acronym...) [Note: It's under
+Control Panel | Network | TCP/IP | WINS Configuration] there's a little text
+entry field called something like 'Scope ID'.
+
+This field essentially creates 'invisible' sub-workgroups on the same wire.
+Boxes can only see other boxes whose Scope IDs are set to the exact same
+value - it's sometimes used by OEMs to configure their boxes to browse only
+other boxes from the same vendor and, in most environments, this field should
+be left blank. If you, in fact, have something in this box that EXACT value
+(case-sensitive!) needs to be provided to smbclient and nmbd as the -i
+(lowercase) parameter. So, if your Scope ID is configured as the string
+'SomeStr' in Win95 then you'd have to use smbclient -iSomeStr <otherparms>
+in connecting to it.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+* 3: How do I synchronize my PC's clock with my Samba server?
+
+To syncronize your PC's clock with your Samba server:
+
+* Copy timesync.pif to your windows directory
+ * timesync.pif can be found at:
+ http://samba.canberra.edu.au/pub/samba/binaries/miscellaneous/timesync.pif
+* Add timesync.pif to your 'Start Up' group/folder
+* Open the properties dialog box for the program/icon
+ * Make sure the 'Run Minimized' option is set in program 'Properties'
+ * Change the command line section that reads \\sambahost to reflect the name
+ of your server.
+* Close the properties dialog box by choosing 'OK'
+
+Each time you start your computer (or login for Win95) your PC will
+synchronize it's clock with your Samba server.
+
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+* 4: Problems with WinDD, NTrigue, WinCenterPro etc
+
+All of the above programs are applications that sit on an NT box and
+allow multiple users to access the NT GUI applications from remote
+workstations (often over X).
+
+What has this got to do with Samba? The problem comes when these users
+use filemanager to mount shares from a Samba server. The most common
+symptom is that the first user to connect get correct file permissions
+and has a nice day, but subsequent connections get logged in as the
+same user as the first person to login. They find that they cannot
+access files in their own home directory, but that they can access
+files in the first users home directory (maybe not such a nice day
+after all?)
+
+Why does this happen? The above products all share a common heritage
+(and code base I believe). They all open just a single TCP based SMB
+connection to the Samba server, and requests from all users are piped
+over this connection. This is unfortunate, but not fatal.
+
+It means that if you run your Samba server in share level security
+(the default) then things will definately break as described above. The
+share level SMB security model has no provision for multiple user IDs
+on the one SMB connection. See security_level.txt in the docs for more
+info on share/user/server level security.
+
+If you run in user or server level security then you have a chance,
+but only if you have a recent version of Samba (at least 1.9.15p6). In
+older versions bugs in Samba meant you still would have had problems.
+
+If you have a trapdoor uid system in your OS then it will never work
+properly. Samba needs to be able to switch uids on the connection and
+it can't if your OS has a trapdoor uid system. You'll know this
+because Samba will note it in your logs.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+* 5: Problem with printers under NT
+
+This info from Stefan Hergeth may be useful:
+
+ A network-printer (with ethernetcard) is connected to the NT-Clients via
+ our UNIX-Fileserver (SAMBA-Server), like the configuration told by
+ Matthew Harrell <harrell@leech.nrl.navy.mil> (see WinNT.txt)
+
+ 1.) If a user has choosen this printer as the default printer in his
+ NT-Session and this printer is not connected to the network
+ (e.g. switched off) than this user has a problem with the SAMBA-
+ connection of his filesystems. It's very slow.
+
+ 2.) If the printer is connected to the network everything works fine.
+
+ 3.) When the smbd ist started with debug level 3, you can see that the
+ NT spooling system try to connect to the printer many times. If the
+ printer ist not connected to the network this request fails and the
+ NT spooler is wasting a lot of time to connect to the printer service.
+ This seems to be the reason for the slow network connection.
+
+ 4.) Maybe it's possible to change this behaviour by setting different printer
+ properties in the Print-Manager-Menu of NT, but i didn't try it
+ yet.
+
+ I hope this information will help in some way.
+
+ Stefan Hergeth <hergeth@f7axp1.informatik.fh-muenchen.de>
+
===============================================================================
SECTION FIVE: Specific client application problems
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+* 1: MS Office Setup reports "Cannot change properties of the file named:
+ X:\MSOFFICE\SETUP.INI"
+When installing MS Office on a Samba drive for which you have admin user
+permissions, ie. admin users = <username>, you will find the setup program
+unable to complete the installation.
+
+To get around this problem, do the installation without admin user permissions
+The problem is that MS Office Setup checks that a file is rdonly by trying to
+open it for writing.
+
+Admin users can always open a file for writing, as they run as root.
+You just have to install as a non-admin user and then use "chown -R" to fix
+the owner.
===============================================================================
SECTION SIX: Miscellaneous
@@ -394,5 +604,3 @@ SECTION SIX: Miscellaneous
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Maintained By Paul Blackman, Email:ictinus@lake.canberra.edu.au
-
-
diff --git a/docs/samba.lsm b/docs/samba.lsm
new file mode 100644
index 00000000000..eacfda5ba0a
--- /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 = samba.anu.edu.au
+Path1 = pub/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..8cccfcf449e
--- /dev/null
+++ b/docs/textdocs/BUGS.txt
@@ -0,0 +1,120 @@
+This file describes how to report Samba bugs.
+
+>> The email address for bug reports is samba-bugs@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 or the mailing list that I will read it. 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://samba.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.
+
+Have you run through DIAGNOSIS.txt? This is very important.
+
+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..ad3eb449314
--- /dev/null
+++ b/docs/textdocs/DIAGNOSIS.txt
@@ -0,0 +1,243 @@
+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".
+
+Another common cause of these two errors is having something already running
+on port 139, such as Samba (ie smbd is running from inetd already) or something
+like Digital's Pathworks. Check your inetd.conf file before trying to start
+smbd as a daemon, it can avoid a lot of frustration!
+
+
+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..eedd0bf36e1
--- /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://samba.anu.edu.au/pub/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..c9ec69918e5
--- /dev/null
+++ b/docs/textdocs/Support.txt
@@ -0,0 +1,395 @@
+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 Tel:+44 118 9545656
+Philip Hands Computing Ltd. | Mobile: +44 802 242989 Fax:+44 118 9474655
+Unit 1, Cherry Close, Caversham, Reading RG4 8UP ENGLAND
+
+Samba experience:
+ Server platforms: Linux,SVR4,SVR3.2 & Sequent ptx
+ Clients: 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
+------------------------------------------------------------------------------
+
+
+------------------------------------------------------------------------------
+WELLINGTON - NEW ZEALAND
+
+David Gempton
+Computer Consultant
+UNIX & PC Networking specialist
+TTC Technology Training Consulting
+PO Box 5444
+Lambton Quay Wellington
+New Zealand
+Phone (025) 518-574
+Email: ttcdg@cyberspace.co.nz
+------------------------------------------------------------------------------
+
diff --git a/docs/textdocs/UNIX-SMB.txt b/docs/textdocs/UNIX-SMB.txt
new file mode 100644
index 00000000000..92167a9e843
--- /dev/null
+++ b/docs/textdocs/UNIX-SMB.txt
@@ -0,0 +1,223 @@
+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.
+
+Note that you can also get the "trapdoor uid" message for other
+reasons. Please see the FAQ for details.
+
+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/docs/textdocs/security_level.txt b/docs/textdocs/security_level.txt
new file mode 100644
index 00000000000..b565ea79668
--- /dev/null
+++ b/docs/textdocs/security_level.txt
@@ -0,0 +1,78 @@
+Description of SMB security levels.
+----------------------------------
+
+
+A SMB server tells the client at startup what "security level" it is
+running. There are two options "share level" and "user level". Which
+of these two the client receives affects the way the client then tries
+to authenticate itself. It does not directly affect (to any great
+extent) the way the Samba server does security. I know this is
+strange, but it fits in with the client/server approach of SMB. In SMB
+everything is initiated and controlled by the client, and the server
+can only tell the client what is available and whether an action is
+allowed.
+
+I'll describe user level security first, as its simpler. In user level
+security the client will send a "session setup" command directly after
+the protocol negotiation. This contains a username and password. The
+server can either accept or reject that username/password
+combination. Note that at this stage the server has no idea what
+share the client will eventually try to connect to, so it can't base
+the "accept/reject" on anything other than:
+
+- the username/password
+- the machine that the client is coming from
+
+If the server accepts the username/password then the client expects to
+be able to mount any share (using a "tree connection") without
+specifying a password. It expects that all access rights will be as
+the username/password specified in the "session setup".
+
+It is also possible for a client to send multiple "session setup"
+requests. When the server responds it gives the client a "uid" to use
+as an authentication tag for that username/password. The client can
+maintain multiple authentication contexts in this way (WinDD is an
+example of an application that does this)
+
+
+Ok, now for share level security. In share level security (the default
+with samba) the client authenticates itself separately for each
+share. It will send a password along with each "tree connection"
+(share mount). It does not explicitly send a username with this
+operation. The client is expecting a password to be associated with
+each share, independent of the user. This means that samba has to work
+out what username the client probably wants to use. It is never
+explicitly sent the username. A "real" SMB server like NT actually
+associates passwords directly with shares in share level security, but
+samba always uses the unix authentication scheme where it is a
+username/password that is authenticated, not a "share/password".
+
+Many clients send a "session setup" even if the server is in share
+level security. They normally send a valid username but no
+password. Samba records this username is a list of "possible
+usernames". When the client then does a "tree connection" it also adds
+to this list the name of the share they try to connect to (useful for
+home directories) and any users listed in the "user =" smb.conf
+line. The password is then checked in turn against these "possible
+usernames". If a match is found then the client is authenticated as
+that user.
+
+Finally "server level" security. In server level security the samba
+server reports to the client that it is in user level security. The
+client then does a "session setup" as described earlier. The samba
+server takes the username/password that the client sends and attempts
+to login to the "password server" by sending exactly the same
+username/password that it got from the client. If that server is in
+user level security and accepts the password then samba accepts the
+clients connection. This allows the samba server to use another SMB
+server as the "password server".
+
+You should also note that at the very start of all this, where the
+server tells the client what security level it is in, it also tells
+the client if it supports encryption. If it does then it supplies the
+client with a random "cryptkey". The client will then send all
+passwords in encrypted form. You have to compile samba with encryption
+enabled to support this feature, and you have to maintain a separate
+smbpasswd file with SMB style encrypted passwords. It is
+cryptographically impossible to translate from unix style encryption
+to SMB style encryption.
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..363e7f67af5
--- /dev/null
+++ b/examples/misc/extra_smbstatus
@@ -0,0 +1,50 @@
+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 if ($1 == "-cs") then
+ echo There are `smbstatus |awk '$1==share {n++;} END {print n}' share=$2` concurrent connections to share: $2
+else if ($1 == "-csl") then
+ echo `date '+ %d/%m/%y %H:%M:%S'` `smbstatus |awk '$1==share {n++;} END {print n}' share=$2` >>$3
+else
+ echo "'smbstat -c' ==> Count unique smbd processes."
+ echo "'smbstat -p' ==> List unique smbd processes."
+ echo "'smbstat -l logfile' ==> Append a log entry for the number of"
+ echo " concurrent and unique processes to logfile."
+ echo "'smbstat -cs sharename'"
+ echo " ==> Count processes connected to sharename (assumed unique)"
+ echo "'smbstat -csl sharename logfile'"
+ echo " ==> Append a log entry for the number of concurrent"
+ echo " processes connected to sharename (assumed unique)"
+endif
+******
+
+Run this script from cron eg.
+
+0,5,10,15,20,25,30,35,40,50,55 * * * * /usr/local/samba/bin/smbstat -l /usr/local/samba/var/smbdcount.log
+
+and you get a good idea of usage over time.
+
+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..9303658ce14
--- /dev/null
+++ b/examples/misc/wall.perl
@@ -0,0 +1,69 @@
+#!/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>
+#
+# Cleanup and corrections by
+# Michal Jaegermann <michal@ellpspace.math.ualberta.ca>
+# Message to send can be now also fed (quietly) from stdin; a pipe will do.
+#=============================================================================
+
+$smbstatus = "/usr/local/bin/smbstatus";
+$smbshout = "/usr/local/bin/smbclient -M";
+
+if (@ARGV) {
+ @clients = @ARGV;
+ undef @ARGV;
+}
+else { # no clients specified explicitly
+ open(PCLIST, "$smbstatus |") || die "$smbstatus failed!.\n$!\n";
+ while(<PCLIST>) {
+ last if /^Locked files:/;
+ split(' ', $_, 6);
+ # do not accept this line if less then six fields
+ next unless $_[5];
+ # if you have A LOT of clients you may speed things up by
+ # checking pid - no need to look further if this pid was already
+ # seen; left as an exercise :-)
+ $client = $_[4];
+ next unless $client =~ /^\w+\./; # expect 'dot' in a client name
+ next if grep($_ eq $client, @clients); # we want this name once
+ push(@clients, $client);
+ }
+ close(PCLIST);
+}
+
+if (-t) {
+ print <<'EOT';
+
+Enter message for Samba clients of this host
+(terminated with single '.' or end of file):
+EOT
+
+ while (<>) {
+ last if /^\.$/;
+ push(@message, $_);
+ }
+}
+else { # keep quiet and read message from stdin
+ @message = <>;
+}
+
+foreach(@clients) {
+## print "To $_:\n";
+ if (open(SENDMSG,"|$smbshout $_")) {
+ print SENDMSG @message;
+ close(SENDMSG);
+ }
+ else {
+ warn "Cannot notify $_ with $smbshout:\n$!\n";
+ }
+}
+
+exit 0;
+
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/thoralf/smb.conf b/examples/thoralf/smb.conf
new file mode 100644
index 00000000000..f9f147474a8
--- /dev/null
+++ b/examples/thoralf/smb.conf
@@ -0,0 +1,152 @@
+; Configuration file for smbd (Samba 1.9.15p8)
+; created by Thoralf Freitag. Send comments to:
+; <Thoralf.Freitag@remserv.rz.fhtw-berlin.de> or
+; <Thoralf.Freitag@t-online.de>
+; last edit 24.04.1995 01:11
+;
+;
+
+[global]
+
+ protocol = NT1
+ ;long filenames for win95
+ mangle case = yes
+ ;lower and upper letters
+ mangled names = yes
+ default case = lower
+ case sensitive = no
+ preserve case = yes
+ short preserve case = yes
+
+ printing = bsd
+ printcap name = /etc/printcap
+ lpq cache time = 0
+ workgroup = WORKGROUP
+ admin users = su
+ ;su is allowed to do all !!!
+ guest account = ftp
+ ;guest is same as user ftp
+ default service = reference
+ ;is possibly helpful to browsing under win 95
+ os level = 2
+ log file = /var/adm/log.smb
+ max log size = 10
+ debug level = 1
+ share modes = yes
+ lock directory = /var/adm
+
+[JP_360_raw]
+ comment = Networkprinter queue for Olivetti JP 360 (untreated RAW format)
+ browseable = yes
+ available = yes
+ public = no
+ force user = root
+ writable = no
+ printable = yes
+ printer name = samba
+ ;samba is an alias name for an raw_printer in your /etc/printcap
+ path = /samba/tmp
+ create mode = 0700
+
+[JP_360_mono]
+ comment = Networkprinter queue for Olivetti JP 360 Mono (with apsfilter)
+ browseable = yes
+ available = yes
+ public = no
+ force user = root
+ writable = no
+ printable = yes
+ printer name = lp
+ ;lp means the standard printer in your /etc/printcap
+ path = /samba/tmp
+ create mode = 0700
+
+[JP_360_color]
+ comment = Networkprinter queue for Olivetti JP 360 Color (with apsfilter)
+ browseable = yes
+ available = yes
+ public = no
+ force user = root
+ writable = no
+ printable = yes
+ printer name = lp4
+ ;my printer need this to print with his color cartridge
+ ;--> the lpd is drive to the printer as an color printer
+ path = /samba/tmp
+ create mode = 0700
+
+[tmp]
+ comment = the garbage dump
+ browseable = yes
+ available = yes
+ public = yes
+ read only = no
+ printable = no
+ path = /samba/tmp
+ create mask = 0777
+
+[transfer]
+ comment = the market place
+ browseable = yes
+ available = yes
+ public = yes
+ read only = no
+ printable = no
+ path = /samba/transfer
+ create mask = 0777
+
+[homes]
+ comment = home directories
+ browseable = no
+ ;ONLY the home-dirs are visible, not the service itself
+ available = yes
+ guest ok = no
+ read only = no
+ printable = no
+ create mode = 0700
+
+[install]
+ comment = all of the many install files
+ browsable = yes
+ available = yes
+ public = no
+ username = @root, @users
+ writable = yes
+ read list = @users
+ printable = no
+ path = /samba/install
+ create mode = 0755
+
+[doc-help]
+ comment = documentations, helpfiles, FAQ's
+ browsable = yes
+ available = yes
+ public = no
+ username = @root, @users
+ writable = yes
+ read list = @users
+ printable = no
+ path = /samba/doc
+ create mode = 0755
+
+[cd_rom_2]
+ comment = the CD in the CD-ROM drive on PANDORA
+ browsable = yes
+ available = yes
+ public = yes
+ writable = no
+ printable = no
+ path = /cdrom
+
+[reference]
+ ;the default, if invalid accesses
+ comment = PANDORA: Samba LAN manager
+ browsable = yes
+ ;only as an hint
+ available = no
+ ;however no access possible
+ public = yes
+ writable = no
+ printable = no
+ path = /samba/tmp
+
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/.cvsignore b/source/.cvsignore
new file mode 100644
index 00000000000..4e181a40a7f
--- /dev/null
+++ b/source/.cvsignore
@@ -0,0 +1,10 @@
+makefile
+nmbd
+nmblookup
+smbclient
+smbd
+smbpasswd
+smbrun
+smbstatus
+testparm
+testprns
diff --git a/source/change-log b/source/change-log
new file mode 100644
index 00000000000..ee49663ed6e
--- /dev/null
+++ b/source/change-log
@@ -0,0 +1,1875 @@
+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
+
+NOTE: From now on the cvs.log file will be used to give a complete log of
+changes to samba. This change-log is now obsolete.
+
+
+==========
+todo:
+
+
+64 bit longs and IP addresses may give problems with unsigned longs?
+
+set archive bit whenever file is modified??
+
+fix man page dates
+
+reply only to own workgroup in server enum
+
+patch to compile with g++ and possibly solaris c++
+
+nmbd needs to keep browse list uptodate by talking to the master if it loses
+an election as others may still think its a valid backup and use it to get
+lists.
+
+leftover lock files can end up belonging to non-smbd processes after a reboot.
+
+hosts allow in nmbd
+
+hosts allow cache
+
+add password command in smbclient
+
+drag long filename to samba under os/2 gives short name
+
+document max ttl option
+
+dup/close 0 for getopt?
+
+implement SMBmove ??
+
+add option to print more info about locked files (full path, share name
+etc)
+
+very slow listing CD, perhaps because of order of stat and readdir or add
+masking to opendir?
+
+protocol drop back in client to avoid openX etc.
+
+handle exported fat drives to a long filename capable client
+
+add check for existance of lpq commands etc (use stat?)
+
+get rid of the silly +4 and -4 by removing NBT stuff
+
+write-only shares
+
+document cnvchar stuff
+
+allow smbd to serve user and group lists to win95
+
+document homes behaviour with WinDD
+
+add "hide file = *.o" "hide dir = .Foo*" "show file = xx*" type options.
+
+ALLOW_PASSWORD_CHANGE only compiles/works on some systems
+
+weird foooooooo/open.exe bug on NT
+
+%a detection can't detect Win95 versus WinNT
+
+reverse mangled maps, so (*.html *.htm) works for new files.
+
+install problems with w95. could be some sort of race?
+
+more efficient Files[] structure to handle thousands of open files
+
+lpd stuff:
+ Tony Aiuto (tony@ics.com)
+
+make max disk size local
+ \ No newline at end of file
diff --git a/source/client/client.c b/source/client/client.c
new file mode 100644
index 00000000000..5e464ea1115
--- /dev/null
+++ b/source/client/client.c
@@ -0,0 +1,4608 @@
+/*
+ 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"
+
+#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;
+char *cmdstr="";
+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;
+struct in_addr ipzero;
+
+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;
+
+
+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);
+static BOOL receive_trans_response(char *inbuf,int trans,
+ int *data_len,int *param_len,
+ char **data,char **param);
+static int interpret_long_filename(int level,char *p,file_info *finfo);
+static void dir_action(char *inbuf,char *outbuf,int attribute,file_info *finfo,BOOL recurse_dir,void (*fn)(),BOOL longdir);
+static int interpret_short_filename(char *p,file_info *finfo);
+static BOOL call_api(int prcnt,int drcnt,
+ int mprcnt,int mdrcnt,
+ int *rprcnt,int *rdrcnt,
+ char *param,char *data,
+ char **rparam,char **rdata);
+
+
+/* clitar bits insert */
+extern int blocksize;
+extern BOOL tar_inc;
+extern BOOL tar_reset;
+/* 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
+
+/****************************************************************************
+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))));
+}
+
+
+/****************************************************************************
+ 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);
+}
+
+
+/****************************************************************************
+ 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);
+}
+
+
+
+/****************************************************************************
+ 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);
+ }
+ }
+}
+
+
+/****************************************************************************
+ 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);
+}
+
+
+/****************************************************************************
+ 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 (!chkpath(rname,False) && !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))));
+ }
+ 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)),
+ -(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;
+ }
+
+ if (Protocol <= PROTOCOL_CORE) {
+ set_message(outbuf,0,6 + strlen(service) + passlen + strlen(dev),True);
+ CVAL(outbuf,smb_com) = SMBtcon;
+ setup_pkt(outbuf);
+
+ p = smb_buf(outbuf);
+ *p++ = 0x04;
+ strcpy(p, service);
+ p = skip_string(p,1);
+ *p++ = 0x04;
+ memcpy(p,pword,passlen);
+ p += passlen;
+ *p++ = 0x04;
+ strcpy(p, dev);
+ }
+ else {
+ 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);
+ }
+
+
+ if (Protocol <= PROTOCOL_CORE) {
+ max_xmit = SVAL(inbuf,smb_vwv0);
+
+ cnum = SVAL(inbuf,smb_vwv1);
+ }
+ else {
+ 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;
+ BOOL ok = False;
+
+ /* 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:"");
+
+ ok=True;
+ 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:"");
+
+ ok=True;
+ p2 += 26;
+ }
+ }
+ }
+
+ if (rparam) free(rparam);
+ if (rdata) free(rdata);
+
+ return(ok);
+}
+
+
+
+
+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
+****************************************************************************/
+static BOOL process(char *base_directory)
+{
+ extern FILE *dbf;
+ pstring line;
+ char *cmd;
+
+ 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);
+
+ cmd = cmdstr;
+ if (cmd[0] != '\0') while (cmd[0] != '\0')
+ {
+ char *p;
+ fstring tok;
+ int i;
+
+ if ((p = strchr(cmd, ';')) == 0)
+ {
+ strncpy(line, cmd, 999);
+ line[1000] = '\0';
+ cmd += strlen(cmd);
+ }
+ else
+ {
+ if (p - cmd > 999) p = cmd + 999;
+ strncpy(line, cmd, p - cmd);
+ line[p - cmd] = '\0';
+ cmd = p + 1;
+ }
+
+ /* input language code to internal one */
+ CNV_INPUT (line);
+
+ /* 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)));
+ }
+ else 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
+****************************************************************************/
+static 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"));
+ DEBUG(0,("\t-c command string execute semicolon separated commands\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 = SMB_PORT;
+ 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();
+
+ ipzero = *interpret_addr2("0.0.0.0");
+
+ 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);
+#endif
+ while ((opt =
+ getopt(argc, argv,"B:O:M:i:Nn:d:Pp:l:hI:EB:U:L:t:m:W:T:D:c:")) != EOF)
+ 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 'c':
+ cmdstr = optarg;
+ got_pass = True;
+ break;
+ case 'h':
+ usage(pname);
+ exit(0);
+ break;
+ case 't':
+#ifdef KANJI
+ if (!setup_term_code (optarg)) {
+ DEBUG(0, ("%s: unknown terminal code name\n", optarg));
+ usage (pname);
+ exit (1);
+ }
+#endif
+ break;
+ 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/clientutil.c b/source/client/clientutil.c
new file mode 100644
index 00000000000..41c482196ad
--- /dev/null
+++ b/source/client/clientutil.c
@@ -0,0 +1,1029 @@
+/*
+ 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"
+
+#ifndef REGISTER
+#define REGISTER 0
+#endif
+
+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;
+
+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)
+
+int name_type = 0x20;
+
+int max_protocol = PROTOCOL_NT1;
+
+BOOL readbraw_supported = False;
+BOOL writebraw_supported = False;
+
+extern int DEBUGLEVEL;
+
+int cnum = 0;
+int pid = 0;
+int gid = 0;
+int uid = 0;
+int mid = 0;
+
+int max_xmit = BUFFER_SIZE;
+
+BOOL have_ip = False;
+
+struct in_addr dest_ip;
+
+extern int Protocol;
+
+extern int Client;
+
+
+/****************************************************************************
+setup basics in a outgoing packet
+****************************************************************************/
+void cli_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);
+ }
+}
+
+/****************************************************************************
+ receive a SMB trans or trans2 response allocating the necessary memory
+ ****************************************************************************/
+BOOL cli_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);
+}
+
+/****************************************************************************
+send a session request
+****************************************************************************/
+BOOL cli_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(5,("Retargeted\n"));
+
+ set_socket_options(Client,user_socket_options);
+
+ /* Try again */
+ return cli_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);
+}
+
+
+static 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}
+ };
+
+/****************************************************************************
+send a login command
+****************************************************************************/
+BOOL cli_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;
+ 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);
+ }
+
+ strcpy(dev,"A:");
+ if (connect_as_printer)
+ strcpy(dev,"LPT1:");
+ if (connect_as_ipc)
+ strcpy(dev,"IPC");
+
+
+ if (start_session && !cli_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;
+ cli_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(5,("max mux %d\n",SVAL(inbuf,smb_vwv3)));
+ max_vcs = SVAL(inbuf,smb_vwv4);
+ DEBUG(5,("max vcs %d\n",max_vcs));
+ DEBUG(5,("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(5,("max mux %d\n",SVAL(inbuf,smb_vwv1+1)));
+ max_vcs = SVAL(inbuf,smb_vwv2+1);
+ DEBUG(5,("max vcs %d\n",max_vcs));
+ DEBUG(5,("max raw %d\n",IVAL(inbuf,smb_vwv5+1)));
+ DEBUG(5,("capabilities 0x%x\n",IVAL(inbuf,smb_vwv9+1)));
+ }
+
+ DEBUG(5,("Sec mode %d\n",SVAL(inbuf,smb_vwv1)));
+ DEBUG(5,("max xmt %d\n",max_xmit));
+ DEBUG(5,("Got %d byte crypt key\n",crypt_len));
+ DEBUG(5,("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)),
+ -(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(5,("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;
+ cli_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;
+ cli_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(5,("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 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(5,("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;
+ cli_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(5,("Connected with cnum=%d max_xmit=%d\n",cnum,max_xmit));
+
+ if (was_null)
+ {
+ free(inbuf);
+ free(outbuf);
+ }
+ return True;
+}
+
+
+/****************************************************************************
+send a logout command
+****************************************************************************/
+void cli_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);
+ cli_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
+****************************************************************************/
+BOOL cli_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);
+
+ cli_send_trans_request(outbuf,SMBtrans,"\\PIPE\\LANMAN",0,0,
+ data,param,NULL,
+ drcnt,prcnt,0,
+ mdrcnt,mprcnt,0);
+
+ return (cli_receive_trans_response(inbuf,SMBtrans,
+ rdrcnt,rprcnt,
+ rdata,rparam));
+}
+
+/****************************************************************************
+ send a SMB trans or trans2 request
+ ****************************************************************************/
+BOOL cli_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);
+ cli_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);
+}
+
+
+/****************************************************************************
+open the client sockets
+****************************************************************************/
+BOOL cli_open_sockets(int port)
+{
+ static int last_port;
+ char *host;
+ pstring service2;
+ extern int Client;
+
+ if (port == 0) port=last_port;
+ last_port=port;
+
+ strupper(service);
+
+ if (*desthost)
+ {
+ host = desthost;
+ }
+ else
+ {
+ strcpy(service2,service);
+ host = strtok(service2,"\\/");
+ strcpy(desthost,host);
+ }
+
+ DEBUG(5,("Opening sockets\n"));
+
+ if (*myname == 0)
+ {
+ get_myname(myname,NULL);
+ strupper(myname);
+ }
+
+ if (!have_ip)
+ {
+ struct hostent *hp;
+
+ if ((hp = Get_Hostbyname(host)) == 0)
+ {
+ DEBUG(0,("Get_Hostbyname: Unknown host %s.\n",host));
+ return False;
+ }
+
+ putip((char *)&dest_ip,(char *)hp->h_addr);
+ }
+
+ Client = open_socket_out(SOCK_STREAM, &dest_ip, port);
+ if (Client == -1)
+ return False;
+
+ DEBUG(5,("Connected\n"));
+
+ set_socket_options(Client,user_socket_options);
+
+ return True;
+}
+
+/****************************************************************************
+close and open the connection again
+****************************************************************************/
+BOOL cli_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);
+ cli_setup_pkt(outbuf);
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,SHORT_TIMEOUT);
+
+ close_sockets();
+ if (!cli_open_sockets(0)) return(False);
+
+ return(cli_send_login(inbuf,outbuf,True,True));
+}
+
+/* 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."},
+ {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..2de09c66c11
--- /dev/null
+++ b/source/client/clitar.c
@@ -0,0 +1,1707 @@
+/*
+ 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 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)),
+ 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))));
+ 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..14b6ec2020f
--- /dev/null
+++ b/source/include/charset.h
@@ -0,0 +1,65 @@
+/*
+ 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')
+
+/* this is used to determine if a character is safe to use in
+ something that may be put on a command line */
+#define issafe(c) (isalnum(c) || strchr("-._",c))
+#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..5b29b275475
--- /dev/null
+++ b/source/include/includes.h
@@ -0,0 +1,1164 @@
+#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
+
+#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
+#if _LINUX_C_LIB_VERSION_MAJOR >= 5
+#define USE_SETFS
+#endif
+#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
+#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
+/* we undef this because sys/param.h is broken in aix. uggh. */
+#undef MAXHOSTNAMELEN
+#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 SEQUENT_PTX4
+#include <string.h>
+#include <sys/dir.h>
+#include <dirent.h>
+#include <sys/statfs.h>
+#include <sys/statvfs.h>
+#include <sys/vfs.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
+#ifndef seteuid
+#define seteuid(uid) setreuid(-1,uid)
+#endif
+#ifndef setegid
+#define setegid(gid) setregid(-1,gid)
+#endif
+#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
+#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 "nameserv.h"
+#include "proto.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..2cfacd66b39
--- /dev/null
+++ b/source/include/local.h
@@ -0,0 +1,164 @@
+/* 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"
+
+/* 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 (2)
+#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
+
+/* keep the password server open, this uses up a aocket, but is needed
+ by many apps */
+#define KEEP_PASSWORD_SERVER_OPEN 1
+
+#endif
diff --git a/source/include/nameserv.h b/source/include/nameserv.h
new file mode 100644
index 00000000000..32ee625fa4f
--- /dev/null
+++ b/source/include/nameserv.h
@@ -0,0 +1,273 @@
+/*
+ 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.
+
+*/
+
+/* NTAS uses 2, NT uses 1, WfWg uses 0 */
+#define MAINTAIN_LIST 2
+#define ELECTION_VERSION 1
+
+#define MAX_DGRAM_SIZE (80*18+64)
+#define MIN_DGRAM_SIZE 12
+
+#define NMB_QUERY 0x20
+#define NMB_STATUS 0x21
+#define NMB_REG 0x05
+#define NMB_REL 0x06
+
+#define NB_GROUP 0x80
+#define NB_PERM 0x02
+#define NB_ACTIVE 0x04
+#define NB_CONFL 0x08
+#define NB_DEREG 0x10
+#define NB_BFLAG 0x00
+#define NB_PFLAG 0x20
+#define NB_MFLAG 0x40
+#define NB__FLAG 0x60
+#define NB_FLGMSK 0x60
+
+#define REFRESH_TIME (15*60)
+
+#define NAME_PERMANENT(p) ((p) & NB_PERM)
+#define NAME_ACTIVE(p) ((p) & NB_ACTIVE)
+#define NAME_CONFLICT(p) ((p) & NB_CONFL)
+#define NAME_DEREG(p) ((p) & NB_DEREG)
+#define NAME_GROUP(p) ((p) & NB_GROUP)
+
+#define NAME_BFLAG(p) (((p) & NB_FLGMSK) == NB_BFLAG)
+#define NAME_PFLAG(p) (((p) & NB_FLGMSK) == NB_PFLAG)
+#define NAME_MFLAG(p) (((p) & NB_FLGMSK) == NB_MFLAG)
+#define NAME__FLAG(p) (((p) & NB_FLGMSK) == NB__FLAG)
+
+enum name_source {STATUS_QUERY, 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};
+enum cmd_type
+{
+ NAME_STATUS_MASTER_CHECK,
+ NAME_STATUS_CHECK,
+ MASTER_SERVER_CHECK,
+ SERVER_CHECK,
+ FIND_MASTER,
+ CHECK_MASTER,
+ NAME_REGISTER,
+ NAME_RELEASE,
+ NAME_CONFIRM_QUERY
+};
+
+/* 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;
+ int nb_flags;
+ enum name_source source;
+};
+
+/* browse and backup server cache for synchronising browse list */
+struct browse_cache_record
+{
+ struct browse_cache_record *next;
+ struct browse_cache_record *prev;
+
+ pstring name;
+ int type;
+ pstring group;
+ struct in_addr ip;
+ time_t sync_time;
+ BOOL synced;
+};
+
+/* this is used to hold the list of servers in my domain, and is */
+/* contained within lists of domains */
+struct server_record
+{
+ struct server_record *next;
+ struct server_record *prev;
+
+ struct server_info_struct serv;
+ time_t death_time;
+};
+
+/* a workgroup structure. it contains a list of servers */
+struct work_record
+{
+ struct work_record *next;
+ struct work_record *prev;
+
+ struct server_record *serverlist;
+
+ /* work group info */
+ fstring work_group;
+ int token; /* used when communicating with backup browsers */
+ int ServerType;
+
+ /* announce info */
+ time_t lastannounce_time;
+ int announce_interval;
+ BOOL needannounce;
+
+ /* election info */
+ BOOL RunningElection;
+ BOOL needelection;
+ int ElectionCount;
+ uint32 ElectionCriterion;
+};
+
+/* a domain structure. it contains a list of workgroups */
+struct domain_record
+{
+ struct domain_record *next;
+ struct domain_record *prev;
+
+ struct work_record *workgrouplist;
+
+ struct in_addr bcast_ip;
+ struct in_addr mask_ip;
+ struct in_addr myip;
+};
+
+/* 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;
+};
+
+
+/* initiated name queries recorded in this list to track any responses... */
+struct name_response_record
+{
+ struct name_response_record *next;
+ struct name_response_record *prev;
+
+ uint16 response_id;
+ enum cmd_type cmd_type;
+
+ int fd;
+ struct nmb_name name;
+ BOOL bcast;
+ BOOL recurse;
+ struct in_addr to_ip;
+
+ time_t start_time;
+ int num_msgs;
+};
+
+/* 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;
+};
+
+
+#define AM_MASTER(work) (work->ServerType & SV_TYPE_MASTER_BROWSER)
+#define AM_BACKUP(work) (work->ServerType & SV_TYPE_BACKUP_BROWSER)
+#define AM_DOMCTL(work) (work->ServerType & SV_TYPE_DOMAIN_CTRL)
+
+
+#define ANN_HostAnnouncement 1
+#define ANN_AnnouncementRequest 2
+#define ANN_Election 8
+#define ANN_GetBackupListReq 9
+#define ANN_GetBackupListResp 10
+#define ANN_BecomeBackup 11
+#define ANN_DomainAnnouncement 12
+#define ANN_MasterAnnouncement 13
+#define ANN_ResetBrowserState 14
+#define ANN_LocalMasterAnnouncement 15
+
diff --git a/source/include/proto.h b/source/include/proto.h
new file mode 100644
index 00000000000..a3f522b2740
--- /dev/null
+++ b/source/include/proto.h
@@ -0,0 +1,512 @@
+BOOL check_access(int snum);
+BOOL allow_access(char *deny_list,char *allow_list,struct from_host *client);
+BOOL fromhost(int sock,struct from_host *f);
+char *unix2dos_format(char *str,BOOL overwrite);
+char *dos2unix_format(char *str, BOOL overwrite);
+int interpret_character_set(char *str, int def);
+void charset_initialise(void);
+void add_char_string(char *s);
+BOOL chat_with_program(char *passwordprogram,char *name,char *chatsequence);
+BOOL chgpasswd(char *name,char *oldpass,char *newpass);
+BOOL chgpasswd(char *name,char *oldpass,char *newpass);
+void setup_pkt(char *outbuf);
+void do_dir(char *inbuf,char *outbuf,char *Mask,int attribute,void (*fn)(),BOOL recurse_dir);
+void cmd_help(void);
+BOOL reopen_connection(char *inbuf,char *outbuf);
+char *smb_errstr(char *inbuf);
+void cli_setup_pkt(char *outbuf);
+BOOL cli_receive_trans_response(char *inbuf,int trans,int *data_len,
+ int *param_len, char **data,char **param);
+BOOL cli_send_session_request(char *inbuf, char *outbuf);
+BOOL cli_send_login(char *inbuf, char *outbuf, BOOL start_session, BOOL use_setup);
+void cli_send_logout(void);
+BOOL cli_call_api(int prcnt,int drcnt,int mprcnt,int mdrcnt,int *rprcnt,
+ int *rdrcnt, char *param,char *data, char **rparam,char **rdata);
+BOOL cli_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);
+BOOL cli_open_sockets(int port);
+BOOL cli_reopen_connection(char *inbuf,char *outbuf);
+char *smb_errstr(char *inbuf);
+int strslashcmp(const char *s1, const char *s2);
+void cmd_block(void);
+void cmd_tarmode(void);
+void cmd_setmode(void);
+void cmd_tar(char *inbuf, char *outbuf);
+int process_tar(char *inbuf, char *outbuf);
+int clipfind(char **aret, int ret, char *tok);
+int tar_parseargs(int argc, char *argv[], char *Optarg, int Optind);
+void init_dptrs(void);
+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_closecnum(int cnum);
+void dptr_idlecnum(int cnum);
+void dptr_closepath(char *path,int pid);
+int dptr_create(int cnum,char *path, BOOL expect_close,int pid);
+BOOL dptr_fill(char *buf1,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 dir_check_ftype(int cnum,int mode,struct stat *st,int dirtype);
+BOOL get_dir_entry(int cnum,char *mask,int dirtype,char *fname,int *size,int *mode,time_t *date,BOOL check_descend);
+void *OpenDir(char *name);
+void CloseDir(void *p);
+char *ReadDirName(void *p);
+BOOL SeekDir(void *p,int pos);
+int TellDir(void *p);
+void DirCacheAdd(char *path,char *name,char *dname,int snum);
+char *DirCacheCheck(char *path,char *name,int snum);
+void DirCacheFlush(int snum);
+void fault_setup(void (*fn)());
+char *getsmbpass(char *prompt) ;
+int reply_trans(char *inbuf,char *outbuf);
+int interpret_coding_system(char *str, int def);
+char *lp_string(char *s);
+BOOL lp_add_home(char *pszHomename, int iDefaultService, char *pszHomedir);
+int lp_add_service(char *pszService, int iDefaultService);
+BOOL lp_add_printer(char *pszPrintername, int iDefaultService);
+BOOL lp_file_list_changed(void);
+BOOL lp_snum_ok(int iService);
+BOOL lp_loaded(void);
+void lp_killunused(BOOL (*snumused)(int ));
+BOOL lp_load(char *pszFname,BOOL global_only);
+int lp_numservices(void);
+void lp_dump(void);
+int lp_servicenumber(char *pszServiceName);
+char *my_workgroup(void);
+char *volume_label(int snum);
+BOOL is_locked(int fnum,int cnum,uint32 count,uint32 offset);
+BOOL do_lock(int fnum,int cnum,uint32 count,uint32 offset,int *eclass,uint32 *ecode);
+BOOL do_unlock(int fnum,int cnum,uint32 count,uint32 offset,int *eclass,uint32 *ecode);
+int get_share_mode_by_fnum(int cnum,int fnum,int *pid);
+int get_share_mode_byname(int cnum,char *fname,int *pid);
+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);
+void clean_share_files(void);
+int str_checksum(char *s);
+BOOL is_8_3(char *fname);
+void create_mangled_stack(int size);
+BOOL check_mangled_stack(char *s);
+BOOL is_mangled(char *s);
+void mangle_name_83(char *s);
+BOOL name_map_mangle(char *OutName,BOOL need83,int snum);
+int reply_sends(char *inbuf,char *outbuf);
+int reply_sendstrt(char *inbuf,char *outbuf);
+int reply_sendtxt(char *inbuf,char *outbuf);
+int reply_sendend(char *inbuf,char *outbuf);
+void announce_request(struct work_record *work, struct in_addr ip);
+void do_announce_request(char *info, char *to_name, int announce_type,
+ int from,
+ int to, struct in_addr dest_ip);
+void announce_backup(void);
+void announce_host(void);
+void announce_master(void);
+struct work_record *remove_workgroup(struct domain_record *d,
+ struct work_record *work);
+void expire_browse_cache(time_t t);
+struct work_record *find_workgroupstruct(struct domain_record *d, fstring name, BOOL add);
+struct domain_record *find_domain(struct in_addr source_ip);
+struct domain_record *add_domain_entry(struct in_addr source_ip,
+ struct in_addr source_mask,
+ char *name, BOOL add);
+struct browse_cache_record *add_browser_entry(char *name, int type, char *wg,
+ time_t ttl, struct in_addr ip);
+struct server_record *add_server_entry(struct domain_record *d,
+ struct work_record *work,
+ char *name,int servertype,
+ int ttl,char *comment,
+ BOOL replace);
+void write_browse_list(void);
+void expire_servers(time_t t);
+void check_master_browser(void);
+void browser_gone(char *work_name, struct in_addr ip);
+void send_election(struct domain_record *d, char *group,uint32 criterion,
+ int timeup,char *name);
+void become_nonmaster(struct domain_record *d, struct work_record *work);
+void run_elections(void);
+void process_election(struct packet_struct *p,char *buf);
+BOOL check_elections(void);
+BOOL name_status(int fd,char *name,int name_type,BOOL recurse,
+ struct in_addr to_ip,char *master,char *rname,
+ void (*fn)());
+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)());
+void expire_netbios_response_entries(time_t t);
+void reply_netbios_packet(struct packet_struct *p1,int trn_id,int rcode,int opcode,
+ struct nmb_name *rr_name,int rr_type,int rr_class,int ttl,
+ char *data,int len);
+uint16 initiate_netbios_packet(int fd,int quest_type,char *name,int name_type,
+ int nb_flags,BOOL bcast,BOOL recurse,struct in_addr to_ip);
+void queue_netbios_pkt_wins(int fd,int quest_type,enum cmd_type cmd,
+ char *name,int name_type,int nb_flags,
+ BOOL bcast,BOOL recurse,struct in_addr to_ip);
+void queue_netbios_packet(int fd,int quest_type,enum cmd_type cmd,char *name,
+ int name_type,int nb_flags,BOOL bcast,BOOL recurse,
+ struct in_addr to_ip);
+struct name_response_record *find_name_query(uint16 id);
+void queue_packet(struct packet_struct *packet);
+void run_packet_queue();
+void listen_for_packets(BOOL run_election);
+BOOL interpret_node_status(char *p, struct nmb_name *name,int t,
+ char *serv_name, struct in_addr ip);
+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);
+void remove_name(struct name_record *n);
+void dump_names(void);
+void remove_netbios_name(char *name,int type, enum name_source source,
+ struct in_addr ip);
+struct name_record *add_netbios_entry(char *name, int type, int nb_flags, int ttl,
+ enum name_source source, struct in_addr ip);
+void remove_name_entry(char *name,int type);
+void add_name_entry(char *name,int type,int nb_flags);
+void add_my_names(void);
+void refresh_my_names(time_t t);
+void expire_names(time_t t);
+void response_name_release(struct packet_struct *p);
+void reply_name_release(struct packet_struct *p);
+void response_name_reg(struct packet_struct *p);
+void reply_name_reg(struct packet_struct *p);
+void reply_name_status(struct packet_struct *p);
+struct name_record *search_for_name(struct nmb_name *question,
+ struct in_addr ip, int Time, int search);
+void process_nmb(struct packet_struct *p);
+void reset_server(char *name, int state, struct in_addr ip);
+void tell_become_backup(void);
+void do_browser_lists(void);
+void sync_server(enum cmd_type cmd, char *serv_name, char *work_name,
+ int name_type,
+ struct in_addr ip);
+void update_from_reg(char *name, int type, struct in_addr ip);
+void add_my_domains(void);
+BOOL same_context(struct dgram_packet *dgram);
+BOOL listening_name(struct work_record *work, struct nmb_name *n);
+void process_logon_packet(struct packet_struct *p,char *buf,int len);
+BOOL listening_type(struct packet_struct *p, int command);
+void process_browse_packet(struct packet_struct *p,char *buf,int len);
+void process_dgram(struct packet_struct *p);
+BOOL reload_services(BOOL test);
+void debug_nmb_packet(struct packet_struct *p);
+char *namestr(struct nmb_name *n);
+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);
+void make_nmb_name(struct nmb_name *n,char *name,int type,char *this_scope);
+BOOL send_packet(struct packet_struct *p);
+struct packet_struct *receive_packet(int fd,enum packet_type type,int t);
+int main(int argc,char *argv[]);
+char *getsmbpass(char *pass);
+void sync_browse_lists(struct work_record *work, char *name, int nm_type,
+ struct in_addr ip);
+BOOL pm_process(char *pszFileName,BOOL (*sfunc)(char *),BOOL (*pfunc)(char *,char *));
+void generate_next_challenge(char *challenge);
+BOOL set_challenge(char *challenge);
+BOOL last_challenge(char *challenge);
+int valid_uid(int uid);
+user_struct *get_valid_user_struct(int uid);
+void invalidate_uid(int uid);
+char *validated_username(int vuid);
+void register_uid(int uid,int gid, char *name,BOOL guest);
+void add_session_user(char *user);
+void dfs_unlogin(void);
+BOOL password_check(char *password);
+BOOL smb_password_check(char *password, unsigned char *part_passwd, unsigned char *c8);
+BOOL password_ok(char *user,char *password, int pwlen, struct passwd *pwd, BOOL is_nt_password);
+BOOL user_ok(char *user,int snum);
+BOOL authorise_login(int snum,char *user,char *password, int pwlen,
+ BOOL *guest,BOOL *force,int vuid);
+BOOL check_hosts_equiv(char *user);
+BOOL server_cryptkey(char *buf);
+BOOL server_validate(char *buf);
+BOOL pcap_printername_ok(char *pszPrintername, char *pszPrintcapname);
+void pcap_printer_fn(void (*fn)());
+void lpq_reset(int snum);
+void print_file(int fnum);
+int get_printqueue(int snum,int cnum,print_queue_struct **queue,
+ print_status_struct *status);
+void del_printqueue(int cnum,int snum,int jobid);
+void status_printjob(int cnum,int snum,int jobid,int status);
+int reply_special(char *inbuf,char *outbuf);
+int reply_tcon(char *inbuf,char *outbuf);
+int reply_tcon_and_X(char *inbuf,char *outbuf,int length,int bufsize);
+int reply_unknown(char *inbuf,char *outbuf);
+int reply_ioctl(char *inbuf,char *outbuf);
+int reply_sesssetup_and_X(char *inbuf,char *outbuf,int length,int bufsize);
+int reply_chkpth(char *inbuf,char *outbuf);
+int reply_getatr(char *inbuf,char *outbuf);
+int reply_setatr(char *inbuf,char *outbuf);
+int reply_dskattr(char *inbuf,char *outbuf);
+int reply_search(char *inbuf,char *outbuf);
+int reply_fclose(char *inbuf,char *outbuf);
+int reply_open(char *inbuf,char *outbuf);
+int reply_open_and_X(char *inbuf,char *outbuf,int length,int bufsize);
+int reply_ulogoffX(char *inbuf,char *outbuf,int length,int bufsize);
+int reply_mknew(char *inbuf,char *outbuf);
+int reply_ctemp(char *inbuf,char *outbuf);
+int reply_unlink(char *inbuf,char *outbuf);
+int reply_readbraw(char *inbuf, char *outbuf);
+int reply_lockread(char *inbuf,char *outbuf);
+int reply_read(char *inbuf,char *outbuf);
+int reply_read_and_X(char *inbuf,char *outbuf,int length,int bufsize);
+int reply_writebraw(char *inbuf,char *outbuf);
+int reply_writeunlock(char *inbuf,char *outbuf);
+int reply_write(char *inbuf,char *outbuf,int dum1,int dum2);
+int reply_write_and_X(char *inbuf,char *outbuf,int length,int bufsize);
+int reply_lseek(char *inbuf,char *outbuf);
+int reply_flush(char *inbuf,char *outbuf);
+int reply_exit(char *inbuf,char *outbuf);
+int reply_close(char *inbuf,char *outbuf);
+int reply_writeclose(char *inbuf,char *outbuf);
+int reply_lock(char *inbuf,char *outbuf);
+int reply_unlock(char *inbuf,char *outbuf);
+int reply_tdis(char *inbuf,char *outbuf);
+int reply_echo(char *inbuf,char *outbuf);
+int reply_printopen(char *inbuf,char *outbuf);
+int reply_printclose(char *inbuf,char *outbuf);
+int reply_printqueue(char *inbuf,char *outbuf);
+int reply_printwrite(char *inbuf,char *outbuf);
+int reply_mkdir(char *inbuf,char *outbuf);
+int reply_rmdir(char *inbuf,char *outbuf);
+int reply_mv(char *inbuf,char *outbuf);
+int reply_copy(char *inbuf,char *outbuf);
+int reply_setdir(char *inbuf,char *outbuf);
+int reply_lockingX(char *inbuf,char *outbuf,int length,int bufsize);
+int reply_readbmpx(char *inbuf,char *outbuf,int length,int bufsize);
+int reply_writebmpx(char *inbuf,char *outbuf);
+int reply_writebs(char *inbuf,char *outbuf);
+int reply_setattrE(char *inbuf,char *outbuf);
+int reply_getattrE(char *inbuf,char *outbuf);
+mode_t unix_mode(int cnum,int dosmode);
+int dos_mode(int cnum,char *path,struct stat *sbuf);
+int dos_chmod(int cnum,char *fname,int dosmode,struct stat *st);
+BOOL unix_convert(char *name,int cnum);
+int disk_free(char *path,int *bsize,int *dfree,int *dsize);
+int sys_disk_free(char *path,int *bsize,int *dfree,int *dsize);
+BOOL check_name(char *name,int cnum);
+void open_file(int fnum,int cnum,char *fname1,int flags,int mode);
+void sync_file(int fnum);
+void close_file(int fnum);
+BOOL check_file_sharing(int cnum,char *fname);
+void open_file_shared(int fnum,int cnum,char *fname,int share_mode,int ofun,
+ int mode,int *Access,int *action);
+int seek_file(int fnum,int pos);
+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 become_service(int cnum,BOOL do_chdir);
+int find_service(char *service);
+int cached_error_packet(char *inbuf,char *outbuf,int fnum,int line);
+int unix_error_packet(char *inbuf,char *outbuf,int def_class,uint32 def_code,int line);
+int error_packet(char *inbuf,char *outbuf,int error_class,uint32 error_code,int line);
+BOOL snum_used(int snum);
+BOOL reload_services(BOOL test);
+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);
+int find_free_file(void );
+int reply_corep(char *outbuf);
+int reply_coreplus(char *outbuf);
+int reply_lanman1(char *outbuf);
+int reply_lanman2(char *outbuf);
+int reply_nt1(char *outbuf);
+void parse_connect(char *buf,char *service,char *user,char *password,int *pwlen,char *dev);
+void close_cnum(int cnum, int uid);
+BOOL yield_connection(int cnum,char *name,int max_connections);
+BOOL claim_connection(int cnum,char *name,int max_connections,BOOL Clear);
+void exit_server(char *reason);
+void standard_sub(int cnum,char *s);
+char *smb_fn_name(int type);
+int chain_reply(int type,char *inbuf,char *inbuf2,char *outbuf,char *outbuf2,int size,int bufsize);
+int construct_reply(char *inbuf,char *outbuf,int size,int bufsize);
+void str_to_key(uchar *str,uchar *key);
+void D1(uchar *k, uchar *d, uchar *out);
+void E1(uchar *k, uchar *d, uchar *out);
+void E_P16(uchar *p14,uchar *p16);
+void E_P24(uchar *p21, uchar *c8, uchar *p24);
+void SMBencrypt(uchar *passwd, uchar *c8, uchar *p24);
+void E_md4hash(uchar *passwd, uchar *p16);
+void SMBNTencrypt(uchar *passwd, uchar *c8, uchar *p24);
+void Ucrit_addUsername(pstring username);
+unsigned int Ucrit_checkUsername(pstring username);
+void Ucrit_addPid(int pid);
+unsigned int Ucrit_checkPid(int pid);
+int sys_select(fd_set *fds,struct timeval *tval);
+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_rename(char *from, char *to);
+int sys_chown(char *fname,int uid,int gid);
+int sys_chroot(char *dname);
+int main(int argc, char *argv[]);
+void GetTimeOfDay(struct timeval *tval);
+void TimeInit(void);
+int TimeDiff(time_t t);
+struct tm *LocalTime(time_t *t);
+time_t interpret_long_date(char *p);
+void put_long_date(char *p,time_t t);
+void put_dos_date(char *buf,int offset,time_t unixdate);
+void put_dos_date2(char *buf,int offset,time_t unixdate);
+void put_dos_date3(char *buf,int offset,time_t unixdate);
+time_t make_unix_date(void *date_ptr);
+time_t make_unix_date2(void *date_ptr);
+time_t make_unix_date3(void *date_ptr);
+BOOL set_filetime(char *fname,time_t mtime);
+char *timestring(void );
+int reply_findclose(char *inbuf,char *outbuf,int length,int bufsize);
+int reply_findnclose(char *inbuf,char *outbuf,int length,int bufsize);
+int reply_transs2(char *inbuf,char *outbuf,int length,int bufsize);
+int reply_trans2(char *inbuf,char *outbuf,int length,int bufsize);
+char *ufc_crypt(char *key,char *salt);
+void init_uid(void);
+BOOL become_guest(void);
+BOOL become_user(int cnum, int uid);
+BOOL unbecome_user(void );
+int smbrun(char *cmd,char *outfile);
+char *get_home_dir(char *user);
+void map_username(char *user);
+struct passwd *Get_Pwnam(char *user,BOOL allow_change);
+BOOL user_in_list(char *user,char *list);
+void setup_logging(char *pname,BOOL interactive);
+void reopen_logs(void);
+BOOL fcntl_lock(int fd,int op,uint32 offset,uint32 count,int type);
+int file_lock(char *name,int timeout);
+void file_unlock(int fd);
+BOOL is_a_socket(int fd);
+BOOL next_token(char **ptr,char *buff,char *sep);
+char **toktocliplist(int *ctok, char *sep);
+void *MemMove(void *dest,void *src,int size);
+void array_promote(char *array,int elsize,int element);
+void set_socket_options(int fd, char *options);
+void close_sockets(void );
+BOOL in_group(gid_t group, int current_gid, int ngroups, int *groups);
+char *StrCpy(char *dest,char *src);
+char *StrnCpy(char *dest,const char *src,int n);
+void putip(void *dest,void *src);
+int name_mangle(char *In,char *Out,char name_type);
+BOOL file_exist(char *fname,struct stat *sbuf);
+time_t file_modtime(char *fname);
+BOOL directory_exist(char *dname,struct stat *st);
+uint32 file_size(char *file_name);
+char *attrib_string(int mode);
+int StrCaseCmp(char *s, char *t);
+int StrnCaseCmp(char *s, char *t, int n);
+BOOL strequal(char *s1,char *s2);
+BOOL strnequal(char *s1,char *s2,int n);
+BOOL strcsequal(char *s1,char *s2);
+void strlower(char *s);
+void strupper(char *s);
+void strnorm(char *s);
+BOOL strisnormal(char *s);
+void string_replace(char *s,char oldc,char newc);
+void unix_format(char *fname);
+void dos_format(char *fname);
+void show_msg(char *buf);
+int smb_len(char *buf);
+void _smb_setlen(char *buf,int len);
+void smb_setlen(char *buf,int len);
+int set_message(char *buf,int num_words,int num_bytes,BOOL zero);
+int smb_numwords(char *buf);
+int smb_buflen(char *buf);
+int smb_buf_ofs(char *buf);
+char *smb_buf(char *buf);
+int smb_offset(char *p,char *buf);
+char *skip_string(char *buf,int n);
+BOOL trim_string(char *s,char *front,char *back);
+void dos_clean_name(char *s);
+void unix_clean_name(char *s);
+int ChDir(char *path);
+char *GetWd(char *str);
+BOOL reduce_name(char *s,char *dir,BOOL widelinks);
+void expand_mask(char *Mask,BOOL doext);
+BOOL strhasupper(char *s);
+BOOL strhaslower(char *s);
+int count_chars(char *s,char c);
+void make_dir_struct(char *buf,char *mask,char *fname,unsigned int size,int mode,time_t date);
+void close_low_fds(void);
+int write_socket(int fd,char *buf,int len);
+int read_udp_socket(int fd,char *buf,int len);
+int set_blocking(int fd, BOOL set);
+int read_with_timeout(int fd,char *buf,int mincnt,int maxcnt,long time_out,BOOL exact);
+int read_max_udp(int fd,char *buffer,int bufsize,int maxtime);
+int TvalDiff(struct timeval *tvalold,struct timeval *tvalnew);
+BOOL send_keepalive(int client);
+int read_data(int fd,char *buffer,int N);
+int write_data(int fd,char *buffer,int N);
+int read_predict(int fd,int offset,char *buf,char **ptr,int num);
+void do_read_prediction();
+void invalidate_read_prediction(int fd);
+int transfer_file(int infd,int outfd,int n,char *header,int headlen,int align);
+int read_smb_length(int fd,char *inbuf,int timeout);
+BOOL receive_smb(int fd,char *buffer,int timeout);
+BOOL send_smb(int fd,char *buffer);
+char *name_ptr(char *buf,int ofs);
+int name_extract(char *buf,int ofs,char *name);
+int name_len(char *s);
+BOOL send_one_packet(char *buf,int len,struct in_addr ip,int port,int type);
+void msleep(int t);
+BOOL in_list(char *s,char *list,BOOL casesensitive);
+BOOL string_init(char **dest,char *src);
+void string_free(char **s);
+BOOL string_set(char **dest,char *src);
+BOOL string_sub(char *s,char *pattern,char *insert);
+BOOL do_match(char *str, char *regexp, int case_sig);
+BOOL mask_match(char *str, char *regexp, int case_sig,BOOL trans2);
+void become_daemon(void);
+void get_broadcast(struct in_addr *if_ipaddr,
+ struct in_addr *if_bcast,
+ struct in_addr *if_nmask);
+BOOL yesno(char *p);
+char *fgets_slash(char *s2,int maxlen,FILE *f);
+int set_filelen(int fd, long len);
+int byte_checksum(char *buf,int len);
+char *dirname_dos(char *path,char *buf);
+void *Realloc(void *p,int size);
+void Abort(void );
+BOOL get_myname(char *myname,struct in_addr *ip);
+BOOL ip_equal(struct in_addr ip1,struct in_addr ip2);
+int open_socket_in(int type, int port, int dlevel);
+int open_socket_out(int type, struct in_addr *addr, int port );
+int interpret_protocol(char *str,int def);
+int interpret_security(char *str,int def);
+unsigned long interpret_addr(char *str);
+struct in_addr *interpret_addr2(char *str);
+BOOL zero_ip(struct in_addr ip);
+void standard_sub_basic(char *s);
+BOOL same_net(struct in_addr ip1,struct in_addr ip2,struct in_addr mask);
+int PutUniCode(char *dst,char *src);
+struct hostent *Get_Hostbyname(char *name);
+BOOL process_exists(int pid);
+char *uidtoname(int uid);
+char *gidtoname(int gid);
+void BlockSignals(BOOL block);
+void ajt_panic(void);
+char *readdirname(void *p);
+void *malloc_wrapped(int size,char *file,int line);
+void *realloc_wrapped(void *ptr,int size,char *file,int line);
+void free_wrapped(void *ptr,char *file,int line);
+char *Strstr(char *s, char *p);
+time_t Mktime(struct tm *t);
+int InNetGr(char *group,char *host,char *user,char *dom);
+void *memcpy_wrapped(void *d,void *s,int l,char *fname,int line);
+int VT_Check(char *buffer);
+int VT_Start_utmp(void);
+int VT_Stop_utmp(void);
+void VT_AtExit(void);
+void VT_SigCLD(int sig);
+void VT_SigEXIT(int sig);
+int VT_Start(void);
+int VT_Output(char *Buffer);
+int VT_Input(char *Buffer,int Size);
+void VT_Process(void);
diff --git a/source/include/smb.h b/source/include/smb.h
new file mode 100644
index 00000000000..0be860d6a36
--- /dev/null
+++ b/source/include/smb.h
@@ -0,0 +1,790 @@
+/*
+ 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 NMB_PORT 137
+#define DGRAM_PORT 138
+#define SMB_PORT 139
+
+#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
+
+#ifndef uchar
+#define uchar unsigned char
+#endif
+#ifndef int16
+#define int16 short
+#endif
+#ifndef uint16
+#define uint16 unsigned short
+#endif
+#ifndef uint32
+#define uint32 unsigned int
+#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;
+
+
+struct current_user {
+ int cnum, id;
+ int uid, gid;
+ int ngroups;
+ gid_t *groups;
+ int *igroups;
+};
+
+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;
+
+/* used for server information: client, nameserv and ipc */
+struct server_info_struct
+{
+ fstring name;
+ uint32 type;
+ fstring comment;
+ fstring domain; /* used ONLY in ipc.c NOT namework.c */
+ BOOL server_added; /* used ONLY in ipc.c NOT namework.c */
+};
+
+
+/* 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 */
+};
+
+#ifdef __STDC__
+int Debug1(char *, ...);
+#else
+int Debug1();
+#endif
+
+#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,PRINT_PLP};
+
+
+/* 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..5244a1d9198
--- /dev/null
+++ b/source/include/version.h
@@ -0,0 +1 @@
+#define VERSION "1.9.16alpha6"
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..d9ee551d6a8
--- /dev/null
+++ b/source/lib/charcnv.c
@@ -0,0 +1,123 @@
+/*
+ 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..7ee8c187885
--- /dev/null
+++ b/source/lib/getsmbpass.c
@@ -0,0 +1,165 @@
+/* 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..a77bdea73f0
--- /dev/null
+++ b/source/lib/kanji.c
@@ -0,0 +1,894 @@
+/*
+ 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..bdff075c7e7
--- /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..ac64b37a6fe
--- /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/time.c b/source/lib/time.c
new file mode 100644
index 00000000000..0b6e6df9b07
--- /dev/null
+++ b/source/lib/time.c
@@ -0,0 +1,495 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ time handling 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"
+
+/*
+ This stuff was largely rewritten by Paul Eggert <eggert@twinsun.com>
+ in May 1996
+ */
+
+
+int serverzone=0;
+int extra_time_offset = 0;
+
+extern int DEBUGLEVEL;
+
+#ifndef CHAR_BIT
+#define CHAR_BIT 8
+#endif
+
+#ifndef TIME_T_MIN
+#define TIME_T_MIN (0 < (time_t) -1 ? (time_t) 0 \
+ : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1))
+#endif
+#ifndef TIME_T_MAX
+#define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN)
+#endif
+
+
+
+/*******************************************************************
+a gettimeofday wrapper
+********************************************************************/
+void GetTimeOfDay(struct timeval *tval)
+{
+#ifdef GETTIMEOFDAY1
+ gettimeofday(tval);
+#else
+ gettimeofday(tval,NULL);
+#endif
+}
+
+#define TM_YEAR_BASE 1900
+
+/*******************************************************************
+yield the difference between *A and *B, in seconds, ignoring leap seconds
+********************************************************************/
+static int tm_diff(struct tm *a, struct tm *b)
+{
+ int ay = a->tm_year + (TM_YEAR_BASE - 1);
+ int by = b->tm_year + (TM_YEAR_BASE - 1);
+ int intervening_leap_days =
+ (ay/4 - by/4) - (ay/100 - by/100) + (ay/400 - by/400);
+ int years = ay - by;
+ int days = 365*years + intervening_leap_days + (a->tm_yday - b->tm_yday);
+ int hours = 24*days + (a->tm_hour - b->tm_hour);
+ int minutes = 60*hours + (a->tm_min - b->tm_min);
+ int seconds = 60*minutes + (a->tm_sec - b->tm_sec);
+ return seconds;
+}
+
+/*******************************************************************
+ return the UTC offset in seconds west of UTC
+ ******************************************************************/
+static int TimeZone(time_t t)
+{
+ struct tm tm_utc = *(gmtime(&t));
+ return tm_diff(&tm_utc,localtime(&t));
+}
+
+
+/*******************************************************************
+init the time differences
+********************************************************************/
+void TimeInit(void)
+{
+ serverzone = TimeZone(time(NULL));
+ DEBUG(4,("Serverzone is %d\n",serverzone));
+}
+
+
+/*******************************************************************
+return the same value as TimeZone, but it should be more efficient.
+
+We keep a table of DST offsets to prevent calling localtime() on each
+call of this function. This saves a LOT of time on many unixes.
+
+Updated by Paul Eggert <eggert@twinsun.com>
+********************************************************************/
+static int TimeZoneFaster(time_t t)
+{
+ static struct dst_table {time_t start,end; int zone;} *dst_table = NULL;
+ static int table_size = 0;
+ int i;
+ int zone = 0;
+
+ if (t == 0) t = time(NULL);
+
+ /* Tunis has a 8 day DST region, we need to be careful ... */
+#define MAX_DST_WIDTH (365*24*60*60)
+#define MAX_DST_SKIP (7*24*60*60)
+
+ for (i=0;i<table_size;i++)
+ if (t >= dst_table[i].start && t <= dst_table[i].end) break;
+
+ if (i<table_size) {
+ zone = dst_table[i].zone;
+ } else {
+ time_t low,high;
+
+ zone = TimeZone(t);
+ dst_table = (struct dst_table *)Realloc(dst_table,
+ sizeof(dst_table[0])*(i+1));
+ if (!dst_table) {
+ table_size = 0;
+ } else {
+ table_size++;
+
+ dst_table[i].zone = zone;
+ dst_table[i].start = dst_table[i].end = t;
+
+ /* no entry will cover more than 6 months */
+ low = t - MAX_DST_WIDTH/2;
+ if (t < low)
+ low = TIME_T_MIN;
+
+ /* widen the new entry using two bisection searches */
+ while (low+60*60 < dst_table[i].start) {
+ if (dst_table[i].start - low > MAX_DST_SKIP*2)
+ t = dst_table[i].start - MAX_DST_SKIP;
+ else
+ t = low + (dst_table[i].start-low)/2;
+ if (TimeZone(t) == zone)
+ dst_table[i].start = t;
+ else
+ low = t;
+ }
+
+ high = low + MAX_DST_WIDTH/2;
+ if (high < t)
+ high = TIME_T_MAX;
+
+ while (high-60*60 > dst_table[i].end) {
+ if (high - dst_table[i].end > MAX_DST_SKIP*2)
+ t = dst_table[i].end + MAX_DST_SKIP;
+ else
+ t = high - (high-dst_table[i].end)/2;
+ if (TimeZone(t) == zone)
+ dst_table[i].end = t;
+ else
+ high = t;
+ }
+#if 0
+ DEBUG(1,("Added DST entry from %s ",
+ asctime(localtime(&dst_table[i].start))));
+ DEBUG(1,("to %s (%d)\n",asctime(localtime(&dst_table[i].end)),
+ dst_table[i].zone));
+#endif
+ }
+ }
+ return zone;
+}
+
+/****************************************************************************
+ return the UTC offset in seconds west of UTC, adjusted for extra time offset
+ **************************************************************************/
+int TimeDiff(time_t t)
+{
+ return TimeZoneFaster(t) + 60*extra_time_offset;
+}
+
+
+/****************************************************************************
+ return the UTC offset in seconds west of UTC, adjusted for extra time
+ offset, for a local time value. If ut = lt + LocTimeDiff(lt), then
+ lt = ut - TimeDiff(ut), but the converse does not necessarily hold near
+ daylight savings transitions because some local times are ambiguous.
+ LocTimeDiff(t) equals TimeDiff(t) except near daylight savings transitions.
+ +**************************************************************************/
+static int LocTimeDiff(time_t lte)
+{
+ time_t lt = lte - 60*extra_time_offset;
+ int d = TimeZoneFaster(lt);
+ time_t t = lt + d;
+
+ /* if overflow occurred, ignore all the adjustments so far */
+ if (((lte < lt) ^ (extra_time_offset < 0)) | ((t < lt) ^ (d < 0)))
+ t = lte;
+
+ /* now t should be close enough to the true UTC to yield the right answer */
+ return TimeDiff(t);
+}
+
+
+/****************************************************************************
+try to optimise the localtime call, it can be quite expenive on some machines
+****************************************************************************/
+struct tm *LocalTime(time_t *t)
+{
+ time_t t2 = *t;
+
+ t2 -= TimeDiff(t2);
+
+ return(gmtime(&t2));
+}
+
+
+#define TIME_FIXUP_CONSTANT (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60))
+
+/****************************************************************************
+interpret an 8 byte "filetime" structure to a time_t
+It's originally in "100ns units since jan 1st 1601"
+
+It appears to be kludge-GMT (at least for file listings). This means
+its the GMT you get by taking a localtime and adding the
+serverzone. This is NOT the same as GMT in some cases. This routine
+converts this to real GMT.
+****************************************************************************/
+time_t 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 (!(TIME_T_MIN <= d && d <= TIME_T_MAX))
+ return(0);
+
+ ret = (time_t)(d+0.5);
+
+ /* this takes us from kludge-GMT to real GMT */
+ ret -= serverzone;
+ ret += LocTimeDiff(ret);
+
+ 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);
+}
+
+
+/****************************************************************************
+check if it's a null mtime
+****************************************************************************/
+static BOOL null_mtime(time_t mtime)
+{
+ if (mtime == 0 || mtime == 0xFFFFFFFF || mtime == (time_t)-1)
+ 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);
+
+ 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 -= 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_isdst = -1;
+
+ /* mktime() also does the local to GMT time conversion for us */
+ ret = mktime(&t);
+
+ return(ret);
+}
+
+/*******************************************************************
+like make_unix_date() but the words are reversed
+********************************************************************/
+time_t make_unix_date2(void *date_ptr)
+{
+ uint32 x,x2;
+
+ x = IVAL(date_ptr,0);
+ x2 = ((x&0xFFFF)<<16) | ((x&0xFFFF0000)>>16);
+ SIVAL(&x,0,x2);
+
+ return(make_unix_date((void *)&x));
+}
+
+/*******************************************************************
+ create a unix GMT date from a dos date in 32 bit "unix like" format
+ these generally arrive as localtimes, with corresponding DST
+ ******************************************************************/
+time_t make_unix_date3(void *date_ptr)
+{
+ time_t t = IVAL(date_ptr,0);
+ if (!null_mtime(t))
+ t += LocTimeDiff(t);
+ return(t);
+}
+
+/****************************************************************************
+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);
+}
+
+
+/****************************************************************************
+ return the date and time as a string
+****************************************************************************/
+char *timestring(void )
+{
+ static char TimeBuf[100];
+ time_t t = time(NULL);
+ struct tm *tm = LocalTime(&t);
+
+#ifdef NO_STRFTIME
+ strcpy(TimeBuf, asctime(tm));
+#elif defined(CLIX) || defined(CONVEX)
+ strftime(TimeBuf,100,"%m/%d/%y %I:%M:%S %p",tm);
+#elif defined(AMPM)
+ strftime(TimeBuf,100,"%D %r",tm);
+#elif defined(TZ_TIME)
+ {
+ int zone = TimeDiff(t);
+ int absZoneMinutes = (zone<0 ? -zone : zone) / 60;
+ size_t len = strftime(TimeBuf,sizeof(TimeBuf)-6,"%D %T",tm);
+ sprintf(TimeBuf+len," %c%02d%02d",
+ zone<0?'+':'-',absZoneMinutes/60,absZoneMinutes%60);
+ }
+#else
+ strftime(TimeBuf,100,"%D %T",tm);
+#endif
+ return(TimeBuf);
+}
+
diff --git a/source/lib/ufc.c b/source/lib/ufc.c
new file mode 100644
index 00000000000..ae48a8776d4
--- /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..8c088f306e9
--- /dev/null
+++ b/source/lib/util.c
@@ -0,0 +1,4056 @@
+/*
+ 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;
+
+/* 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;
+ }
+ }
+}
+
+
+/*******************************************************************
+check if the log has grown too big
+********************************************************************/
+static void check_log_size(void)
+{
+ static int debug_count=0;
+ int maxlog;
+ struct stat st;
+
+ if (debug_count++ < 100) return;
+
+ maxlog = lp_max_log_size() * 1024;
+ if (!dbf || maxlog <= 0) return;
+
+ 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;
+}
+
+
+/*******************************************************************
+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;
+
+ if (stdout_logging) {
+#ifdef __STDC__
+ va_start(ap, format_str);
+#else
+ va_start(ap);
+ format_str = va_arg(ap,char *);
+#endif
+ vfprintf(dbf,format_str,ap);
+ va_end(ap);
+ return(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];
+
+#ifdef __STDC__
+ va_start(ap, format_str);
+#else
+ va_start(ap);
+ format_str = va_arg(ap,char *);
+#endif
+ vsprintf(msgbuf, format_str, ap);
+ va_end(ap);
+
+ msgbuf[255] = '\0';
+ syslog(priority, "%s", msgbuf);
+ }
+#endif
+
+#ifdef SYSLOG
+ if (!lp_syslog_only())
+#endif
+ {
+#ifdef __STDC__
+ va_start(ap, format_str);
+#else
+ va_start(ap);
+ format_str = va_arg(ap,char *);
+#endif
+ vfprintf(dbf,format_str,ap);
+ va_end(ap);
+ fflush(dbf);
+ }
+
+ check_log_size();
+
+ 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);
+}
+
+/****************************************************************************
+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;
+}
+
+/****************************************************************************
+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);
+}
+
+/*******************************************************************
+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 (size == 0) {
+ if (p) free(p);
+ DEBUG(5,("Realloc asked for 0 bytes\n"));
+ return NULL;
+ }
+
+ if (!p)
+ ret = (void *)malloc(size);
+ else
+ ret = (void *)realloc(p,size);
+
+ if (!ret)
+ DEBUG(0,("Memory allocation error: failed to expand to %d bytes\n",size));
+
+ return(ret);
+}
+
+#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
+
+
+#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 */
+ if (gethostname(host_name, MAXHOSTNAMELEN) == -1)
+ { 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 == SMB_PORT || port == NMB_PORT)
+ 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);
+}
+
+/*******************************************************************
+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));
+ }
+ }
+}
+
+
+/*******************************************************************
+are two IPs on the same subnet?
+********************************************************************/
+BOOL same_net(struct in_addr ip1,struct in_addr ip2,struct in_addr mask)
+{
+ unsigned long net1,net2,nmask;
+
+ nmask = ntohl(mask.s_addr);
+ net1 = ntohl(ip1.s_addr);
+ net2 = ntohl(ip2.s_addr);
+
+ return((net1 & nmask) == (net2 & nmask));
+}
+
+
+/*******************************************************************
+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);
+}
+
+/****************************************************************************
+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)
+{
+ system("/usr/bin/X11/xedit -display :0 /tmp/ERROR_FAULT &");
+}
+#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(char *group,char *host,char *user,char *dom)
+{
+ char *hst, *usr, *dm;
+
+ setnetgrent(group);
+ while (getnetgrent(&hst, &usr, &dm))
+ if (((host == 0) || (hst == 0) || !strcmp(host, hst)) &&
+ ((user == 0) || (usr == 0) || !strcmp(user, usr)) &&
+ ((dom == 0) || (dm == 0) || !strcmp(dom, dm))) {
+ endnetgrent();
+ return (1);
+ }
+ endnetgrent();
+ return (0);
+}
+#endif
+
+
+#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/namequery.c b/source/libsmb/namequery.c
new file mode 100644
index 00000000000..d1b1ae7d3e5
--- /dev/null
+++ b/source/libsmb/namequery.c
@@ -0,0 +1,292 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ name query 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"
+
+extern pstring scope;
+extern int DEBUGLEVEL;
+
+
+/****************************************************************************
+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;
+ int i;
+ *flags = 0;
+ StrnCpy(qname,p,15);
+ type = CVAL(p,15);
+ p += 16;
+
+ strcat(flags, (p[0] & 0x80) ? "<GROUP> " : " ");
+ if ((p[0] & 0x60) == 0x00) strcat(flags,"B ");
+ if ((p[0] & 0x60) == 0x20) strcat(flags,"P ");
+ if ((p[0] & 0x60) == 0x40) strcat(flags,"M ");
+ if ((p[0] & 0x60) == 0x60) 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," ");
+ }
+
+ for (i = strlen( qname) ; --i >= 0 ; ) {
+ if (!isprint(qname[i])) qname[i] = '.';
+ }
+ DEBUG(level,("\t%-15s <%02x> - %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;
+ static int name_trn_id = 0;
+
+ 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 = 0;
+ nmb->header.nm_flags.recursion_desired = 1;
+ 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;
+ static int name_trn_id = 0;
+
+ 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 = 0;
+ nmb->header.nm_flags.recursion_desired = 1;
+ 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);
+}
diff --git a/source/libsmb/nmblib.c b/source/libsmb/nmblib.c
new file mode 100644
index 00000000000..d82d89f6534
--- /dev/null
+++ b/source/libsmb/nmblib.c
@@ -0,0 +1,697 @@
+/*
+ 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 "localnet.h"
+#include "loadparm.h"
+
+extern struct in_addr myip;
+extern int DEBUGLEVEL;
+
+int num_good_sends = 0;
+int num_good_receives = 0;
+extern pstring scope;
+extern pstring myname;
+extern struct in_addr ipzero;
+
+
+/****************************************************************************
+ print out a res_rec structure
+ ****************************************************************************/
+static void debug_nmb_res_rec(struct res_rec *res, char *hdr)
+{
+ int i, j;
+
+ DEBUG(4,(" %s: nmb_name=%s rr_type=%d rr_class=%d ttl=%d\n",
+ hdr,
+ namestr(&res->rr_name),
+ res->rr_type,
+ res->rr_class,
+ res->ttl));
+
+ if (res->rdlength == 0 || res->rdata == NULL) return;
+
+ for (i = 0; i < res->rdlength; i+= 16)
+ {
+ DEBUG(4, (" %s %3x char ", hdr, i));
+
+ for (j = 0; j < 16; j++)
+ {
+ unsigned char x = res->rdata[i+j];
+ if (x < 32 || x > 127) x = '.';
+
+ if (i+j >= res->rdlength) break;
+ DEBUG(4, ("%c", x));
+ }
+
+ DEBUG(4, (" hex ", i));
+
+ for (j = 0; j < 16; j++)
+ {
+ if (i+j >= res->rdlength) break;
+ DEBUG(4, ("%02X", (unsigned char)res->rdata[i+j]));
+ }
+
+ DEBUG(4, ("\n"));
+ }
+}
+
+/****************************************************************************
+ process a nmb packet
+ ****************************************************************************/
+void debug_nmb_packet(struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+
+ DEBUG(4,("nmb packet from %s header: id=%d opcode=%d response=%s\n",
+ inet_ntoa(p->ip),
+ nmb->header.name_trn_id,nmb->header.opcode,BOOLSTR(nmb->header.response)));
+ DEBUG(4,(" header: flags: bcast=%s rec_avail=%s rec_des=%s trunc=%s auth=%s\n",
+ BOOLSTR(nmb->header.nm_flags.bcast),
+ BOOLSTR(nmb->header.nm_flags.recursion_available),
+ BOOLSTR(nmb->header.nm_flags.recursion_desired),
+ BOOLSTR(nmb->header.nm_flags.trunc),
+ BOOLSTR(nmb->header.nm_flags.authoritative)));
+ DEBUG(4,(" header: rcode=%d qdcount=%d ancount=%d nscount=%d arcount=%d\n",
+ nmb->header.rcode,
+ nmb->header.qdcount,
+ nmb->header.ancount,
+ nmb->header.nscount,
+ nmb->header.arcount));
+
+ if (nmb->header.qdcount)
+ {
+ DEBUG(4,(" question: q_name=%s q_type=%d q_class=%d\n",
+ namestr(&nmb->question.question_name),
+ nmb->question.question_type,
+ nmb->question.question_class));
+ }
+
+ if (nmb->answers && nmb->header.ancount)
+ {
+ debug_nmb_res_rec(nmb->answers,"answers");
+ }
+ if (nmb->nsrecs && nmb->header.nscount)
+ {
+ debug_nmb_res_rec(nmb->nsrecs,"nsrecs");
+ }
+ if (nmb->additional && nmb->header.arcount)
+ {
+ debug_nmb_res_rec(nmb->additional,"additional");
+ }
+}
+
+/*******************************************************************
+ handle "compressed" name pointers
+ ******************************************************************/
+static BOOL handle_name_ptrs(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 and parse some resource records
+ ******************************************************************/
+static BOOL parse_alloc_res_rec(char *inbuf,int *offset,int length,
+ struct res_rec **recs, int count)
+{
+ int i;
+ *recs = (struct res_rec *)malloc(sizeof(**recs)*count);
+ if (!*recs) return(False);
+
+ 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(5,("%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(5,("%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);
+}
+
+
diff --git a/source/libsmb/smbencrypt.c b/source/libsmb/smbencrypt.c
new file mode 100644
index 00000000000..be22fc50fc6
--- /dev/null
+++ b/source/libsmb/smbencrypt.c
@@ -0,0 +1,189 @@
+#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;
+
+#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((char *)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/loadparm.h b/source/loadparm.h
index 513dbcd636b..92c8274767d 100644
--- a/source/loadparm.h
+++ b/source/loadparm.h
@@ -43,6 +43,7 @@ extern char *lp_guestaccount(int iService);
extern char *lp_printcapname(void);
extern char *lp_lockdir(void);
extern char *lp_logfile(void);
+extern char *lp_smbrun(void);
extern char *lp_configfile(void);
extern char *lp_smb_passwd_file(void);
extern char *lp_rootdir(void);
@@ -55,6 +56,7 @@ extern char *lp_domain_controller(void);
extern char *lp_username_map(void);
extern char *lp_hosts_equiv(void);
extern char *lp_logon_script(void);
+extern char *lp_wins_server(void);
extern char *lp_magicscript(int iService);
extern char *lp_magicoutput(int iService);
extern char *lp_mangled_map(int iService);
@@ -65,6 +67,8 @@ extern int lp_max_log_size(void);
extern int lp_maxxmit(void);
extern int lp_maxmux(void);
extern int lp_mangledstack(void);
+extern BOOL lp_wins_support(void);
+extern BOOL lp_wins_proxy(void);
extern BOOL lp_preferred_master(void);
extern BOOL lp_domain_master(void);
extern BOOL lp_domain_logons(void);
@@ -154,6 +158,7 @@ extern char lp_magicchar(int iService);
extern int lp_max_connections(int iService);
extern BOOL lp_add_home(char *pservice,int ifrom,char *phome);
extern char *lp_string(char *s);
+extern BOOL lp_delete_readonly(int iService);
char *my_workgroup(void);
#endif
diff --git a/source/localnet.h b/source/localnet.h
new file mode 100644
index 00000000000..7f335e790e4
--- /dev/null
+++ b/source/localnet.h
@@ -0,0 +1,6 @@
+extern struct in_addr myip;
+extern struct in_addr bcast_ip;
+extern struct in_addr Netmask;
+
+extern int ClientNMB;
+extern int ClientDGRAM;
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/nameannounce.c b/source/nameannounce.c
new file mode 100644
index 00000000000..6b086c97746
--- /dev/null
+++ b/source/nameannounce.c
@@ -0,0 +1,445 @@
+/*
+ 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.
+
+ Revision History:
+
+ 14 jan 96: lkcl@pires.co.uk
+ added multiple workgroup domain master support
+
+*/
+
+#include "includes.h"
+#include "loadparm.h"
+
+#define TEST_CODE
+
+extern int DEBUGLEVEL;
+extern BOOL CanRecurse;
+
+extern struct in_addr myip;
+extern struct in_addr bcast_ip;
+extern struct in_addr Netmask;
+extern struct in_addr ipzero;
+
+extern pstring myname;
+
+extern int ClientDGRAM;
+extern int ClientNMB;
+
+/* this is our domain/workgroup/server database */
+extern struct domain_record *domainlist;
+
+/* machine comment for host announcements */
+extern pstring ServerComment;
+
+extern int updatecount;
+extern int workgroup_count;
+
+/* what server type are we currently */
+
+#define MSBROWSE "\001\002__MSBROWSE__\002"
+#define BROWSE_MAILSLOT "\\MAILSLOT\\BROWSE"
+
+/****************************************************************************
+ send a announce request to the local net
+ **************************************************************************/
+void announce_request(struct work_record *work, struct in_addr ip)
+{
+ pstring outbuf;
+ char *p;
+
+ if (!work) return;
+
+ work->needannounce = True;
+
+ DEBUG(2,("sending announce request to %s for workgroup %s\n",
+ inet_ntoa(ip),work->work_group));
+
+ bzero(outbuf,sizeof(outbuf));
+ p = outbuf;
+ CVAL(p,0) = ANN_AnnouncementRequest;
+ p++;
+
+ CVAL(p,0) = work->token; /* flags?? XXXX probably a token*/
+ p++;
+ StrnCpy(p,myname,16);
+ strupper(p);
+ p = skip_string(p,1);
+
+ send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
+ myname,work->work_group,0x20,0x0,ip,myip);
+}
+
+
+/****************************************************************************
+ request an announcement
+ **************************************************************************/
+void do_announce_request(char *info, char *to_name, int announce_type,
+ int from,
+ int to, struct in_addr dest_ip)
+{
+ pstring outbuf;
+ char *p;
+
+ bzero(outbuf,sizeof(outbuf));
+ p = outbuf;
+ CVAL(p,0) = announce_type;
+ p++;
+
+ DEBUG(2,("sending announce type %d: info %s to %s - server %s(%x)\n",
+ announce_type, info, inet_ntoa(dest_ip),to_name,to));
+
+ StrnCpy(p,info,16);
+ strupper(p);
+ p = skip_string(p,1);
+
+ send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
+ myname,to_name,from,to,dest_ip,myip);
+}
+
+/****************************************************************************
+ construct a host announcement unicast
+ **************************************************************************/
+void announce_backup(void)
+{
+ static time_t lastrun = 0;
+ time_t t = time(NULL);
+ pstring outbuf;
+ char *p;
+ struct domain_record *d1;
+ int tok;
+
+ if (!lastrun) lastrun = t;
+ if (t < lastrun + 1*60) return;
+ lastrun = t;
+
+ for (tok = 0; tok <= workgroup_count; tok++)
+ {
+ for (d1 = domainlist; d1; d1 = d1->next)
+ {
+ struct work_record *work;
+ struct domain_record *d;
+
+ /* search for unique workgroup: only the name matters */
+ for (work = d1->workgrouplist;
+ work && (tok != work->token);
+ work = work->next);
+
+ if (!work) continue;
+
+ /* found one: announce it across all domains */
+ for (d = domainlist; d; d = d->next)
+ {
+ int type=0;
+
+ if (AM_DOMCTL(work)) {
+ type = 0x1b;
+ } else if (AM_MASTER(work)) {
+ type = 0x1d;
+ } else {
+ continue;
+ }
+
+ DEBUG(2,("sending announce backup %s workgroup %s(%d)\n",
+ inet_ntoa(d->bcast_ip),work->work_group,
+ work->token));
+
+ bzero(outbuf,sizeof(outbuf));
+ p = outbuf;
+ CVAL(p,0) = ANN_GetBackupListReq;
+ p++;
+
+ CVAL(p,0) = 1; /* count? */
+ SIVAL(p,1,work->token); /* workgroup unique key index */
+ p += 5;
+ p++;
+
+ send_mailslot_reply(BROWSE_MAILSLOT,
+ ClientDGRAM,outbuf,
+ PTR_DIFF(p,outbuf),
+ myname, work->work_group,
+ 0x0,type,d->bcast_ip,myip);
+ }
+ }
+ }
+}
+
+
+/****************************************************************************
+ construct a host announcement unicast
+ **************************************************************************/
+void announce_host(void)
+{
+ time_t t = time(NULL);
+ pstring outbuf;
+ char *p;
+ char *namep;
+ char *stypep;
+ char *commentp;
+ pstring comment;
+ char *my_name;
+ struct domain_record *d;
+
+ StrnCpy(comment, *ServerComment ? ServerComment : "NoComment", 43);
+
+ my_name = *myname ? myname : "NoName";
+
+ for (d = domainlist; d; d = d->next)
+ {
+ struct work_record *work;
+
+ if (!ip_equal(bcast_ip,d->bcast_ip))
+ continue;
+
+ for (work = d->workgrouplist; work; work = work->next)
+ {
+ uint32 stype = work->ServerType;
+ struct server_record *s;
+ BOOL announce = False;
+
+ if (work->needannounce) {
+ /* drop back to a max 3 minute announce - this is to prevent a
+ single lost packet from stuffing things up for too long */
+ work->announce_interval = MIN(work->announce_interval,3*60);
+ work->lastannounce_time = t - (work->announce_interval+1);
+ }
+
+ /* announce every minute at first then progress to every 12 mins */
+ if (work->lastannounce_time &&
+ (t - work->lastannounce_time) < work->announce_interval)
+ continue;
+
+ if (work->announce_interval < 12*60)
+ work->announce_interval += 60;
+
+ work->lastannounce_time = t;
+
+ if (!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);
+ }
+
+ for (s = work->serverlist; s; s = s->next) {
+ if (strequal(myname, s->serv.name)) {
+ announce = True;
+ break;
+ }
+ }
+
+ if (announce)
+ {
+ bzero(outbuf,sizeof(outbuf));
+ p = outbuf+1;
+
+ CVAL(p,0) = updatecount;
+ /* ms - despite the spec */
+ SIVAL(p,1,work->announce_interval*1000);
+ namep = p+5;
+ StrnCpy(namep,my_name,16);
+ strupper(namep);
+ 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(commentp,comment);
+ p = p+31;
+ p = skip_string(p,1);
+
+ if (ip_equal(bcast_ip,d->bcast_ip))
+ {
+ if (AM_MASTER(work))
+ {
+ SIVAL(stypep,0,work->ServerType);
+
+ DEBUG(2,("sending local master announce to %s for %s\n",
+ inet_ntoa(d->bcast_ip),work->work_group));
+
+ CVAL(outbuf,0) = ANN_LocalMasterAnnouncement;
+
+ send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,
+ PTR_DIFF(p,outbuf),
+ my_name,work->work_group,0,
+ 0x1e,d->bcast_ip,myip);
+
+ DEBUG(2,("sending domain announce to %s for %s\n",
+ inet_ntoa(d->bcast_ip),work->work_group));
+
+ CVAL(outbuf,0) = ANN_DomainAnnouncement;
+
+ StrnCpy(namep,work->work_group,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,0x01,d->bcast_ip,myip);
+ }
+ else
+ {
+ DEBUG(2,("sending host announce to %s for %s\n",
+ inet_ntoa(d->bcast_ip),work->work_group));
+
+ CVAL(outbuf,0) = ANN_HostAnnouncement;
+
+ send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,
+ PTR_DIFF(p,outbuf),
+ my_name,work->work_group,0,0x1d,
+ d->bcast_ip,myip);
+ }
+ }
+ }
+
+ if (work->needannounce)
+ {
+ work->needannounce = False;
+ break;
+ /* sorry: can't do too many announces. do some more later */
+ }
+ }
+ }
+}
+
+
+/****************************************************************************
+ announce myself as a master to all other primary domain conrollers.
+
+ BIG NOTE: this code will remain untested until some kind soul that has access
+ to a couple of windows NT advanced servers runs this version of nmbd for at
+ least 15 minutes.
+
+ this actually gets done in search_and_sync_workgroups() via the
+ MASTER_SERVER_CHECK command, if there is a response from the
+ name query initiated here. see response_name_query()
+ **************************************************************************/
+void announce_master(void)
+{
+ struct domain_record *d;
+ static time_t last=0;
+ time_t t = time(NULL);
+ BOOL am_master = False; /* are we a master of some sort? :-) */
+
+#ifdef TEST_CODE
+ if (last && (t-last < 2*60)) return;
+#else
+ if (last && (t-last < 15*60)) return;
+#endif
+
+ last = t;
+
+ for (d = domainlist; d; d = d->next)
+ {
+ struct work_record *work;
+ for (work = d->workgrouplist; work; work = work->next)
+ {
+ if (AM_MASTER(work))
+ {
+ am_master = True;
+ }
+ }
+ }
+
+ if (!am_master) return; /* only proceed if we are a master browser */
+
+ for (d = domainlist; d; d = d->next)
+ {
+ struct work_record *work;
+ for (work = d->workgrouplist; work; work = work->next)
+ {
+ struct server_record *s;
+ for (s = work->serverlist; s; s = s->next)
+ {
+ if (strequal(s->serv.name, myname)) continue;
+
+ /* all PDCs (which should also be master browsers) */
+ if (s->serv.type & SV_TYPE_DOMAIN_CTRL)
+ {
+ /* check the existence of a pdc for this workgroup, and if
+ one exists at the specified ip, sync with it and announce
+ ourselves as a master browser to it */
+
+ if (!*lp_domain_controller() ||
+ !strequal(lp_domain_controller(), s->serv.name))
+ {
+ if (!lp_wins_support() && *lp_wins_server())
+ {
+ struct in_addr ip;
+ ip = ipzero;
+
+ queue_netbios_pkt_wins(ClientNMB,NMB_QUERY,
+ MASTER_SERVER_CHECK,
+ work->work_group,0x1b,0,
+ False, False, ip);
+ }
+ else
+ {
+ struct domain_record *d2;
+ for (d2 = domainlist; d2; d2 = d2->next)
+ {
+ queue_netbios_packet(ClientNMB,NMB_QUERY,
+ MASTER_SERVER_CHECK,
+ work->work_group,0x1b,0,
+ True, False, d2->bcast_ip);
+ }
+ }
+ }
+ }
+ }
+
+ /* now do primary domain controller - the one that's not
+ necessarily in our browse lists, although it ought to be
+ this pdc is the one that we get TOLD about through smb.conf.
+ basically, if it's on a subnet that we know about, it may end
+ up in our browse lists (which is why it's explicitly excluded
+ in the code above) */
+
+ if (*lp_domain_controller())
+ {
+ struct in_addr ip;
+ BOOL bcast = False;
+
+ ip = *interpret_addr2(lp_domain_controller());
+
+ if (zero_ip(ip))
+ {
+ ip = bcast_ip;
+ bcast = True;
+ }
+
+ DEBUG(2, ("Searching for PDC %s at %s\n",
+ lp_domain_controller(), inet_ntoa(ip)));
+
+ /* check the existence of a pdc for this workgroup, and if
+ one exists at the specified ip, sync with it and announce
+ ourselves as a master browser to it */
+ queue_netbios_pkt_wins(ClientNMB, NMB_QUERY,MASTER_SERVER_CHECK,
+ work->work_group,0x1b, 0,
+ bcast, False, ip);
+ }
+ }
+ }
+}
diff --git a/source/namedb.c b/source/namedb.c
new file mode 100644
index 00000000000..fc14c4d5c60
--- /dev/null
+++ b/source/namedb.c
@@ -0,0 +1,709 @@
+/*
+ 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.
+
+ Revision History:
+
+ 14 jan 96: lkcl@pires.co.uk
+ added multiple workgroup domain master support
+
+*/
+
+#include "includes.h"
+#include "smb.h"
+#include "loadparm.h"
+#include "localnet.h"
+
+extern int DEBUGLEVEL;
+
+extern time_t StartupTime;
+extern pstring myname;
+extern pstring scope;
+extern struct in_addr bcast_ip;
+
+/* this is our browse master/backup cache database */
+struct browse_cache_record *browserlist = NULL;
+
+/* this is our domain/workgroup/server database */
+struct domain_record *domainlist = NULL;
+
+static BOOL updatedlists = True;
+int updatecount=0;
+
+int workgroup_count = 0; /* unique index key: one for each workgroup */
+
+/* what server type are we currently */
+
+#define DFLT_SERVER_TYPE (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 */
+#define MSBROWSE "\001\002__MSBROWSE__\002"
+
+
+/****************************************************************************
+ add a workgroup into the domain list
+ **************************************************************************/
+static void add_workgroup(struct work_record *work, struct domain_record *d)
+{
+ struct work_record *w2;
+
+ if (!work || !d) return;
+
+ if (!d->workgrouplist)
+ {
+ d->workgrouplist = work;
+ work->prev = NULL;
+ work->next = NULL;
+ return;
+ }
+
+ for (w2 = d->workgrouplist; w2->next; w2 = w2->next);
+
+ w2->next = work;
+ work->next = NULL;
+ work->prev = w2;
+}
+
+
+/****************************************************************************
+ create a blank workgroup
+ **************************************************************************/
+static struct work_record *make_workgroup(char *name)
+{
+ struct work_record *work;
+ struct domain_record *d;
+ int t = -1;
+
+ if (!name || !name[0]) return NULL;
+
+ work = (struct work_record *)malloc(sizeof(*work));
+ if (!work) return(NULL);
+
+ StrnCpy(work->work_group,name,sizeof(work->work_group)-1);
+ work->serverlist = NULL;
+
+ work->ServerType = DFLT_SERVER_TYPE;
+ work->RunningElection = False;
+ work->ElectionCount = 0;
+ work->needelection = False;
+ work->needannounce = True;
+
+ /* make sure all token representations of workgroups are unique */
+
+ for (d = domainlist; d && t == -1; d = d->next)
+ {
+ struct work_record *w;
+ for (w = d->workgrouplist; w && t == -1; w = w->next)
+ {
+ if (strequal(w->work_group, work->work_group)) t = w->token;
+ }
+ }
+
+ if (t == -1)
+ {
+ work->token = ++workgroup_count;
+ }
+ else
+ {
+ work->token = t;
+ }
+
+
+ /* WfWg uses 01040b01 */
+ /* Win95 uses 01041501 */
+ /* NTAS uses ???????? */
+ work->ElectionCriterion = (MAINTAIN_LIST<<1)|(ELECTION_VERSION<<8);
+ work->ElectionCriterion |= (lp_os_level() << 24);
+ if (lp_domain_master()) {
+ work->ElectionCriterion |= 0x80;
+ }
+
+ return work;
+}
+
+
+/*******************************************************************
+ expire old servers in the serverlist
+ time of -1 indicates everybody dies
+ ******************************************************************/
+static void remove_old_servers(struct work_record *work, time_t t)
+{
+ struct server_record *s;
+ struct server_record *nexts;
+
+ /* expire old entries in the serverlist */
+ for (s = work->serverlist; s; s = nexts)
+ {
+ if (t == -1 || (s->death_time && s->death_time < t))
+ {
+ DEBUG(3,("Removing dead server %s\n",s->serv.name));
+ updatedlists = True;
+ nexts = s->next;
+
+ if (s->prev) s->prev->next = s->next;
+ if (s->next) s->next->prev = s->prev;
+
+ if (work->serverlist == s)
+ work->serverlist = s->next;
+
+ free(s);
+ }
+ else
+ {
+ nexts = s->next;
+ }
+ }
+}
+
+
+/*******************************************************************
+ remove workgroups
+ ******************************************************************/
+struct work_record *remove_workgroup(struct domain_record *d,
+ struct work_record *work)
+{
+ struct work_record *ret_work = NULL;
+
+ if (!d || !work) return NULL;
+
+ DEBUG(3,("Removing old workgroup %s\n", work->work_group));
+
+ remove_old_servers(work, -1);
+
+ ret_work = work->next;
+
+ if (work->prev) work->prev->next = work->next;
+ if (work->next) work->next->prev = work->prev;
+
+ if (d->workgrouplist == work) d->workgrouplist = work->next;
+
+ free(work);
+
+ return ret_work;
+}
+
+
+/****************************************************************************
+ 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 browser into the list
+ **************************************************************************/
+static void add_browse_cache(struct browse_cache_record *b)
+{
+ struct browse_cache_record *b2;
+
+ if (!browserlist)
+ {
+ browserlist = b;
+ b->prev = NULL;
+ b->next = NULL;
+ return;
+ }
+
+ for (b2 = browserlist; b2->next; b2 = b2->next) ;
+
+ b2->next = b;
+ b->next = NULL;
+ b->prev = b2;
+}
+
+
+/***************************************************************************
+ add a server into the list
+ **************************************************************************/
+static void add_server(struct work_record *work,struct server_record *s)
+{
+ struct server_record *s2;
+
+ if (!work->serverlist) {
+ work->serverlist = s;
+ s->prev = NULL;
+ s->next = NULL;
+ return;
+ }
+
+ for (s2 = work->serverlist; s2->next; s2 = s2->next) ;
+
+ s2->next = s;
+ s->next = NULL;
+ s->prev = s2;
+}
+
+
+/*******************************************************************
+ remove old browse entries
+ ******************************************************************/
+void expire_browse_cache(time_t t)
+{
+ struct browse_cache_record *b;
+ struct browse_cache_record *nextb;
+
+ /* expire old entries in the serverlist */
+ for (b = browserlist; b; b = nextb)
+ {
+ if (b->synced && b->sync_time < t)
+ {
+ DEBUG(3,("Removing dead cached browser %s\n",b->name));
+ nextb = b->next;
+
+ if (b->prev) b->prev->next = b->next;
+ if (b->next) b->next->prev = b->prev;
+
+ if (browserlist == b) browserlist = b->next;
+
+ free(b);
+ }
+ else
+ {
+ nextb = b->next;
+ }
+ }
+}
+
+
+/****************************************************************************
+ find a workgroup in the workgrouplist
+ only create it if the domain allows it, or the parameter 'add' insists
+ that it get created/added anyway. this allows us to force entries in
+ lmhosts file to be added.
+ **************************************************************************/
+struct work_record *find_workgroupstruct(struct domain_record *d, fstring name, BOOL add)
+{
+ struct work_record *ret, *work;
+
+ if (!d) return NULL;
+
+ DEBUG(4, ("workgroup search for %s: ", name));
+
+ if (strequal(name, "*"))
+ {
+ DEBUG(2,("add any workgroups: initiating browser search on %s\n",
+ inet_ntoa(d->bcast_ip)));
+ queue_netbios_pkt_wins(ClientNMB,NMB_QUERY, FIND_MASTER,
+ MSBROWSE,0x1,0,
+ True,False, d->bcast_ip);
+ return NULL;
+ }
+
+ for (ret = d->workgrouplist; ret; ret = ret->next)
+ {
+ if (!strcmp(ret->work_group,name))
+ {
+ DEBUG(4, ("found\n"));
+ return(ret);
+ }
+ }
+
+ DEBUG(4, ("not found: creating\n"));
+
+ if ((work = make_workgroup(name)))
+ {
+ if (lp_preferred_master() &&
+ strequal(lp_workgroup(), name) &&
+ ip_equal(d->bcast_ip, bcast_ip))
+ {
+ DEBUG(3, ("preferred master startup for %s\n", work->work_group));
+ work->needelection = True;
+ work->ElectionCriterion |= (1<<3);
+ }
+ if (!ip_equal(bcast_ip, d->bcast_ip))
+ {
+ work->needelection = False;
+ }
+ add_workgroup(work, d);
+ return(work);
+ }
+ return NULL;
+}
+
+/****************************************************************************
+ find a domain in the domainlist
+ **************************************************************************/
+struct domain_record *find_domain(struct in_addr source_ip)
+{
+ struct domain_record *d;
+
+ /* search through domain list for broadcast/netmask that matches
+ the source ip address */
+
+ for (d = domainlist; d; d = d->next)
+ {
+ if (same_net(source_ip, d->bcast_ip, d->mask_ip))
+ {
+ return(d);
+ }
+ }
+
+ return (NULL);
+}
+
+
+/****************************************************************************
+ dump a copy of the workgroup/domain database
+ **************************************************************************/
+static void dump_workgroups(void)
+{
+ struct domain_record *d;
+
+ for (d = domainlist; d; d = d->next)
+ {
+ if (d->workgrouplist)
+ {
+ struct work_record *work;
+
+ DEBUG(4,("dump domain bcast=%15s: ", inet_ntoa(d->bcast_ip)));
+ DEBUG(4,(" netmask=%15s:\n", inet_ntoa(d->mask_ip)));
+
+ for (work = d->workgrouplist; work; work = work->next)
+ {
+ DEBUG(4,("\t%s(%d)\n", work->work_group, work->token));
+ if (work->serverlist)
+ {
+ struct server_record *s;
+ for (s = work->serverlist; s; s = s->next)
+ {
+ DEBUG(4,("\t\t%s %8x (%s)\n",
+ s->serv.name, s->serv.type, s->serv.comment));
+ }
+ }
+ }
+ }
+ }
+}
+
+/****************************************************************************
+ create a domain entry
+ ****************************************************************************/
+static struct domain_record *make_domain(struct in_addr ip, struct in_addr mask)
+{
+ struct domain_record *d;
+ d = (struct domain_record *)malloc(sizeof(*d));
+
+ if (!d) return(NULL);
+
+ bzero((char *)d,sizeof(*d));
+
+ DEBUG(4, ("making domain %s ", inet_ntoa(ip)));
+ DEBUG(4, ("%s\n", inet_ntoa(mask)));
+
+ d->bcast_ip = ip;
+ d->mask_ip = mask;
+ d->workgrouplist = NULL;
+
+ add_domain(d);
+
+ return d;
+}
+
+/****************************************************************************
+ add a domain entry. creates a workgroup, if necessary, and adds the domain
+ to the named a workgroup.
+ ****************************************************************************/
+struct domain_record *add_domain_entry(struct in_addr source_ip,
+ struct in_addr source_mask,
+ char *name, BOOL add)
+{
+ struct domain_record *d;
+ struct in_addr ip;
+
+ ip = *interpret_addr2("255.255.255.255");
+
+ if (zero_ip(source_ip)) source_ip = bcast_ip;
+
+ /* add the domain into our domain database */
+ if ((d = find_domain(source_ip)) ||
+ (d = make_domain(source_ip, source_mask)))
+ {
+ struct work_record *w = find_workgroupstruct(d, name, add);
+
+ /* add WORKGROUP(1e) and WORKGROUP(00) entries into name database
+ or register with WINS server, if it's our workgroup */
+ if (strequal(lp_workgroup(), name))
+ {
+ extern pstring ServerComment;
+ add_name_entry(name,0x1e,NB_ACTIVE|NB_GROUP);
+ add_name_entry(name,0x0 ,NB_ACTIVE|NB_GROUP);
+ add_server_entry(d,w,myname,w->ServerType,0,ServerComment,True);
+ }
+
+ DEBUG(3,("Added domain name entry %s at %s\n", name,inet_ntoa(ip)));
+ return d;
+ }
+ return NULL;
+}
+
+/****************************************************************************
+ add a browser entry
+ ****************************************************************************/
+struct browse_cache_record *add_browser_entry(char *name, int type, char *wg,
+ time_t ttl, struct in_addr ip)
+{
+ BOOL newentry=False;
+
+ struct browse_cache_record *b;
+
+ /* search for the entry: if it's already in the cache, update that entry */
+ for (b = browserlist; b; b = b->next)
+ {
+ if (ip_equal(ip,b->ip) && strequal(b->group, wg)) break;
+ }
+
+ if (b && b->synced)
+ {
+ /* entries get left in the cache for a while. this stops sync'ing too
+ often if the network is large */
+ DEBUG(4, ("browser %s %s %s already sync'd at time %d\n",
+ b->name, b->group, inet_ntoa(b->ip), b->sync_time));
+ return NULL;
+ }
+
+ if (!b)
+ {
+ newentry = True;
+ b = (struct browse_cache_record *)malloc(sizeof(*b));
+
+ if (!b) return(NULL);
+
+ bzero((char *)b,sizeof(*b));
+ }
+
+ /* update the entry */
+ ttl = time(NULL)+ttl;
+
+ StrnCpy(b->name ,name,sizeof(b->name )-1);
+ StrnCpy(b->group,wg ,sizeof(b->group)-1);
+ strupper(b->name);
+ strupper(b->group);
+
+ b->ip = ip;
+ b->type = type;
+
+ if (newentry || ttl < b->sync_time) b->sync_time = ttl;
+
+ if (newentry)
+ {
+ b->synced = False;
+ add_browse_cache(b);
+
+ DEBUG(3,("Added cache entry %s %s(%2x) %s ttl %d\n",
+ wg, name, type, inet_ntoa(ip),ttl));
+ }
+ else
+ {
+ DEBUG(3,("Updated cache entry %s %s(%2x) %s ttl %d\n",
+ wg, name, type, inet_ntoa(ip),ttl));
+ }
+
+ return(b);
+}
+
+
+/****************************************************************************
+ add a server entry
+ ****************************************************************************/
+struct server_record *add_server_entry(struct domain_record *d,
+ struct work_record *work,
+ char *name,int servertype,
+ int ttl,char *comment,
+ BOOL replace)
+{
+ BOOL newentry=False;
+ struct server_record *s;
+
+ if (name[0] == '*')
+ {
+ return (NULL);
+ }
+
+ for (s = work->serverlist; s; s = s->next)
+ {
+ if (strequal(name,s->serv.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));
+ }
+
+ if (ip_equal(bcast_ip, d->bcast_ip) &&
+ strequal(lp_workgroup(),work->work_group))
+ {
+ servertype |= SV_TYPE_LOCAL_LIST_ONLY;
+ }
+ else
+ {
+ servertype &= ~SV_TYPE_LOCAL_LIST_ONLY;
+ }
+
+ /* update the entry */
+ StrnCpy(s->serv.name,name,sizeof(s->serv.name)-1);
+ StrnCpy(s->serv.comment,comment,sizeof(s->serv.comment)-1);
+ strupper(s->serv.name);
+ s->serv.type = servertype;
+ s->death_time = ttl?time(NULL)+ttl*3:0;
+
+ /* for a domain entry, the comment field refers to the server name */
+
+ if (s->serv.type & SV_TYPE_DOMAIN_ENUM) strupper(s->serv.comment);
+
+ if (newentry)
+ {
+ add_server(work, s);
+
+ DEBUG(3,("Added "));
+ }
+ else
+ {
+ DEBUG(3,("Updated "));
+ }
+
+ DEBUG(3,("server entry %s of type %x (%s) to %s %s\n",
+ name,servertype,comment,
+ work->work_group,inet_ntoa(d->bcast_ip)));
+
+ return(s);
+}
+
+
+/*******************************************************************
+ write out browse.dat
+ ******************************************************************/
+void write_browse_list(void)
+{
+ struct domain_record *d;
+
+ pstring fname,fnamenew;
+ FILE *f;
+
+ if (!updatedlists) return;
+
+ dump_names();
+ dump_workgroups();
+
+ updatedlists = False;
+ 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 (d = domainlist; d ; d = d->next)
+ {
+ struct work_record *work;
+ for (work = d->workgrouplist; work ; work = work->next)
+ {
+ struct server_record *s;
+ for (s = work->serverlist; s ; s = s->next)
+ {
+ fstring tmp;
+
+ /* don't list domains I don't have a master for */
+ if ((s->serv.type & SV_TYPE_DOMAIN_ENUM) &&
+ !s->serv.comment[0])
+ {
+ continue;
+ }
+
+ /* output server details, plus what workgroup/domain
+ they're in. without the domain information, the
+ combined list of all servers in all workgroups gets
+ sent to anyone asking about any workgroup! */
+
+ sprintf(tmp, "\"%s\"", s->serv.name);
+ fprintf(f, "%-25s ", tmp);
+ fprintf(f, "%08x ", s->serv.type);
+ sprintf(tmp, "\"%s\" ", s->serv.comment);
+ fprintf(f, "%-30s", tmp);
+ fprintf(f, "\"%s\"\n", work->work_group);
+ }
+ }
+ }
+
+ fclose(f);
+ unlink(fname);
+ chmod(fnamenew,0644);
+ rename(fnamenew,fname);
+ DEBUG(3,("Wrote browse list %s\n",fname));
+}
+
+
+/*******************************************************************
+ expire old servers in the serverlist
+ ******************************************************************/
+void expire_servers(time_t t)
+{
+ struct domain_record *d;
+
+ for (d = domainlist ; d ; d = d->next)
+ {
+ struct work_record *work;
+
+ for (work = d->workgrouplist; work; work = work->next)
+ {
+ remove_old_servers(work, t);
+ }
+ }
+}
+
diff --git a/source/nameelect.c b/source/nameelect.c
new file mode 100644
index 00000000000..8ceae473a78
--- /dev/null
+++ b/source/nameelect.c
@@ -0,0 +1,369 @@
+/*
+ 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.
+
+ Revision History:
+
+ 14 jan 96: lkcl@pires.co.uk
+ added multiple workgroup domain master support
+
+*/
+
+#include "includes.h"
+#include "loadparm.h"
+#include "localnet.h"
+
+extern int DEBUGLEVEL;
+extern pstring scope;
+
+extern pstring myname;
+
+/* machine comment for host announcements */
+extern pstring ServerComment;
+
+/* here are my election parameters */
+
+extern time_t StartupTime;
+
+#define AM_MASTER(work) (work->ServerType & SV_TYPE_MASTER_BROWSER)
+
+#define MSBROWSE "\001\002__MSBROWSE__\002"
+#define BROWSE_MAILSLOT "\\MAILSLOT\\BROWSE"
+
+extern struct domain_record *domainlist;
+
+
+/*******************************************************************
+ occasionally check to see if the master browser is around
+ ******************************************************************/
+void check_master_browser(void)
+{
+ static time_t lastrun=0;
+ time_t t = time(NULL);
+ struct domain_record *d;
+
+ if (!lastrun) lastrun = t;
+ if (t < lastrun + 2*60) return;
+ lastrun = t;
+
+ dump_workgroups();
+
+ for (d = domainlist; d; d = d->next)
+ {
+ struct work_record *work;
+
+ for (work = d->workgrouplist; work; work = work->next)
+ {
+ /* if we are not the browse master of a workgroup, and we can't
+ find a browser on the subnet, do something about it. */
+
+ if (!AM_MASTER(work))
+ {
+ queue_netbios_packet(ClientNMB,NMB_QUERY,CHECK_MASTER,
+ work->work_group,0x1d,0,
+ True,False,d->bcast_ip);
+ }
+ }
+ }
+}
+
+
+/*******************************************************************
+ what to do if a master browser DOESN't exist
+ ******************************************************************/
+void browser_gone(char *work_name, struct in_addr ip)
+{
+ struct domain_record *d = find_domain(ip);
+ struct work_record *work = find_workgroupstruct(d, work_name, False);
+
+ if (!work || !d) return;
+
+ DEBUG(2,("Forcing election on %s\n",work->work_group));
+
+ if (strequal(work->work_group, lp_workgroup()) &&
+ ip_equal(bcast_ip, d->bcast_ip))
+ {
+ /* we can attempt to become master browser */
+ work->needelection = True;
+ }
+ else
+ {
+ DEBUG(2,("no master browser for persistent entry %s %s\n",
+ work->work_group, inet_ntoa(d->bcast_ip)));
+
+ /* XXXX oh dear. we are going to have problems here. the
+ entry is a persistent one, there isn't anyone responsible
+ for this workgroup up and running, yet we can't find it
+ and we are going to continually have name_queries until
+ a master browser is found for this workgroup on the
+ remote subnet.
+ */
+ }
+}
+/****************************************************************************
+ send an election packet
+ **************************************************************************/
+void send_election(struct domain_record *d, char *group,uint32 criterion,
+ int timeup,char *name)
+{
+ pstring outbuf;
+ char *p;
+
+ if (!d) return;
+
+ DEBUG(2,("Sending election to %s for workgroup %s\n",
+ inet_ntoa(d->bcast_ip),group));
+
+ bzero(outbuf,sizeof(outbuf));
+ p = outbuf;
+ CVAL(p,0) = 8; /* election */
+ p++;
+
+ CVAL(p,0) = (criterion == 0 && timeup == 0) ? 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,d->bcast_ip,myip);
+}
+
+
+/*******************************************************************
+ become the master browser
+ ******************************************************************/
+static void become_master(struct domain_record *d, struct work_record *work)
+{
+ uint32 domain_type = SV_TYPE_DOMAIN_ENUM | SV_TYPE_SERVER_UNIX | 0x00400000;
+
+ if (!work) return;
+
+ DEBUG(2,("Becoming master for %s\n",work->work_group));
+
+ work->ServerType |= SV_TYPE_MASTER_BROWSER;
+ work->ServerType &= ~SV_TYPE_POTENTIAL_BROWSER;
+ work->ElectionCriterion |= 0x5;
+
+ /* add browse, master and general names to database or register with WINS */
+ add_name_entry(MSBROWSE ,0x01,NB_ACTIVE|NB_GROUP);
+ add_name_entry(work->work_group,0x1d,NB_ACTIVE );
+
+ if (lp_domain_master())
+ {
+ DEBUG(4,("Domain master: adding names...\n"));
+
+ /* add domain master and domain member names or register with WINS */
+ add_name_entry(work->work_group,0x1b,NB_ACTIVE );
+ add_name_entry(work->work_group,0x1c,NB_ACTIVE|NB_GROUP);
+
+ work->ServerType |= SV_TYPE_DOMAIN_MASTER;
+
+ if (lp_domain_logons())
+ {
+ work->ServerType |= SV_TYPE_DOMAIN_CTRL;
+ work->ServerType |= SV_TYPE_DOMAIN_MEMBER;
+ }
+ }
+
+ /* update our server status */
+ add_server_entry(d,work,work->work_group,domain_type,0,myname,True);
+ add_server_entry(d,work,myname,work->ServerType,0,ServerComment,True);
+
+ if (ip_equal(bcast_ip, d->bcast_ip))
+ {
+ /* ask all servers on our local net to announce to us */
+ announce_request(work, d->bcast_ip);
+ }
+}
+
+
+/*******************************************************************
+ unbecome the master browser
+ ******************************************************************/
+void become_nonmaster(struct domain_record *d, struct work_record *work)
+{
+ DEBUG(2,("Becoming non-master for %s\n",work->work_group));
+
+ work->ServerType &= ~SV_TYPE_MASTER_BROWSER;
+ work->ServerType &= ~SV_TYPE_DOMAIN_MASTER;
+ work->ServerType |= SV_TYPE_POTENTIAL_BROWSER;
+
+ work->ElectionCriterion &= ~0x4;
+
+ remove_name_entry(work->work_group,0x1b);
+ remove_name_entry(work->work_group,0x1c);
+ remove_name_entry(work->work_group,0x1d);
+ remove_name_entry(MSBROWSE ,0x01);
+}
+
+
+/*******************************************************************
+ run the election
+ ******************************************************************/
+void run_elections(void)
+{
+ time_t t = time(NULL);
+ static time_t lastime = 0;
+
+ struct domain_record *d;
+
+ /* send election packets once a second */
+ if (lastime && t-lastime <= 0) return;
+
+ lastime = t;
+
+ for (d = domainlist; d; d = d->next)
+ {
+ struct work_record *work;
+ for (work = d->workgrouplist; work; work = work->next)
+ {
+ if (work->RunningElection)
+ {
+ send_election(d,work->work_group, work->ElectionCriterion,
+ t-StartupTime,myname);
+
+ if (work->ElectionCount++ >= 4)
+ {
+ /* I won! now what :-) */
+ DEBUG(2,(">>> Won election on %s <<<\n",work->work_group));
+
+ work->RunningElection = False;
+ become_master(d, work);
+ }
+ }
+ }
+ }
+}
+
+
+/*******************************************************************
+ work out if I win an election
+ ******************************************************************/
+static BOOL win_election(struct work_record *work,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 = work->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.
+ ******************************************************************/
+void process_election(struct packet_struct *p,char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ struct in_addr ip = dgram->header.source_ip;
+ struct domain_record *d = find_domain(ip);
+ int version = CVAL(buf,0);
+ uint32 criterion = IVAL(buf,1);
+ int timeup = IVAL(buf,5)/1000;
+ char *name = buf+13;
+ struct work_record *work;
+
+ if (!d) return;
+
+ name[15] = 0;
+
+ DEBUG(3,("Election request from %s vers=%d criterion=%08x timeup=%d\n",
+ name,version,criterion,timeup));
+
+ if (same_context(dgram)) return;
+
+ for (work = d->workgrouplist; work; work = work->next)
+ {
+ if (listening_name(work, &dgram->dest_name) &&
+ strequal(work->work_group, lp_workgroup()) &&
+ ip_equal(d->bcast_ip, bcast_ip))
+ {
+ if (win_election(work, version,criterion,timeup,name))
+ {
+ if (!work->RunningElection)
+ {
+ work->needelection = True;
+ work->ElectionCount=0;
+ }
+ }
+ else
+ {
+ work->needelection = False;
+
+ if (work->RunningElection)
+ {
+ work->RunningElection = False;
+ DEBUG(3,(">>> Lost election on %s <<<\n",work->work_group));
+
+ /* if we are the master then remove our masterly names */
+ if (AM_MASTER(work))
+ {
+ become_nonmaster(d, work);
+ }
+ }
+ }
+ }
+ }
+}
+
+
+/****************************************************************************
+ checks whether a browser election is to be run on any workgroup
+ ***************************************************************************/
+BOOL check_elections(void)
+{
+ struct domain_record *d;
+ BOOL run_any_election = False;
+
+ for (d = domainlist; d; d = d->next)
+ {
+ struct work_record *work;
+ for (work = d->workgrouplist; work; work = work->next)
+ {
+ run_any_election |= work->RunningElection;
+
+ if (work->needelection && !work->RunningElection)
+ {
+ DEBUG(3,(">>> Starting election on %s <<<\n",work->work_group));
+ work->ElectionCount = 0;
+ work->RunningElection = True;
+ work->needelection = False;
+ }
+ }
+ }
+ return run_any_election;
+}
+
diff --git a/source/nameresp.c b/source/nameresp.c
new file mode 100644
index 00000000000..435864a7843
--- /dev/null
+++ b/source/nameresp.c
@@ -0,0 +1,628 @@
+/*
+ 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 "localnet.h"
+#include "loadparm.h"
+
+/* this is our initiated name query response database */
+struct name_response_record *nameresponselist = NULL;
+
+extern struct in_addr myip;
+extern int DEBUGLEVEL;
+
+static uint16 name_trn_id=0;
+BOOL CanRecurse = True;
+extern pstring scope;
+extern pstring myname;
+extern struct in_addr ipzero;
+
+
+/***************************************************************************
+ add an initated name query into the list
+ **************************************************************************/
+extern void add_response_record(struct name_response_record *n)
+{
+ struct name_response_record *n2;
+
+ if (!nameresponselist)
+ {
+ nameresponselist = n;
+ n->prev = NULL;
+ n->next = NULL;
+ return;
+ }
+
+ for (n2 = nameresponselist; n2->next; n2 = n2->next) ;
+
+ n2->next = n;
+ n->next = NULL;
+ n->prev = n2;
+}
+
+
+/*******************************************************************
+ remove old name response entries
+ ******************************************************************/
+void expire_netbios_response_entries(time_t t)
+{
+ struct name_response_record *n;
+ struct name_response_record *nextn;
+
+ for (n = nameresponselist; n; n = nextn)
+ {
+ if (n->start_time < t)
+ {
+ DEBUG(3,("Removing dead name query for %s %s (num_msgs=%d)\n",
+ inet_ntoa(n->to_ip), namestr(&n->name), n->num_msgs));
+
+ if (n->cmd_type == CHECK_MASTER && n->num_msgs == 0)
+ {
+ if (n->num_msgs > 1)
+ {
+ /* more than one master browser detected on a subnet.
+ there is a configuration problem. force an election */
+ struct domain_record *d;
+ if ((d = find_domain(n->to_ip)))
+ {
+ send_election(d,n->name.name,0,0,myname);
+ }
+ }
+
+ /* if no response received, the master browser must have gone */
+ browser_gone(n->name.name, n->to_ip);
+ }
+
+ nextn = n->next;
+
+ if (n->prev) n->prev->next = n->next;
+ if (n->next) n->next->prev = n->prev;
+
+ if (nameresponselist == n) nameresponselist = n->next;
+
+ free(n);
+ }
+ else
+ {
+ nextn = n->next;
+ }
+ }
+}
+
+
+/****************************************************************************
+ reply to a netbios name packet
+ ****************************************************************************/
+void reply_netbios_packet(struct packet_struct *p1,int trn_id,int rcode,int opcode,
+ struct nmb_name *rr_name,int rr_type,int rr_class,int ttl,
+ char *data,int len)
+{
+ struct packet_struct p;
+ struct nmb_packet *nmb = &p.packet.nmb;
+ struct res_rec answers;
+ char *packet_type = "unknown";
+
+ p = *p1;
+
+ if (rr_type == NMB_STATUS) packet_type = "nmb_status";
+ if (rr_type == NMB_QUERY ) packet_type = "nmb_query";
+ if (rr_type == NMB_REG ) packet_type = "nmb_reg";
+ if (rr_type == NMB_REL ) packet_type = "nmb_rel";
+
+ DEBUG(4,("replying netbios packet: %s %s\n",
+ packet_type, namestr(rr_name), inet_ntoa(p.ip)));
+
+ nmb->header.name_trn_id = trn_id;
+ nmb->header.opcode = opcode;
+ nmb->header.response = True;
+ nmb->header.nm_flags.bcast = False;
+ nmb->header.nm_flags.recursion_available = True;
+ nmb->header.nm_flags.recursion_desired = True;
+ nmb->header.nm_flags.trunc = False;
+ nmb->header.nm_flags.authoritative = True;
+
+ nmb->header.qdcount = 0;
+ nmb->header.ancount = 1;
+ nmb->header.nscount = 0;
+ nmb->header.arcount = 0;
+ nmb->header.rcode = 0;
+
+ bzero((char*)&nmb->question,sizeof(nmb->question));
+
+ nmb->answers = &answers;
+ bzero((char*)nmb->answers,sizeof(*nmb->answers));
+
+ nmb->answers->rr_name = *rr_name;
+ nmb->answers->rr_type = rr_type;
+ nmb->answers->rr_class = rr_class;
+ nmb->answers->ttl = ttl;
+
+ if (data && len)
+ {
+ nmb->answers->rdlength = len;
+ memcpy(nmb->answers->rdata, data, len);
+ }
+
+ p.packet_type = NMB_PACKET;
+
+ debug_nmb_packet(&p);
+
+ send_packet(&p);
+}
+
+
+/****************************************************************************
+ initiate a netbios packet
+ ****************************************************************************/
+uint16 initiate_netbios_packet(int fd,int quest_type,char *name,int name_type,
+ int nb_flags,BOOL bcast,BOOL recurse,
+ struct in_addr to_ip)
+{
+ struct packet_struct p;
+ struct nmb_packet *nmb = &p.packet.nmb;
+ struct res_rec additional_rec;
+ char *packet_type = "unknown";
+ int opcode = -1;
+
+ if (quest_type == NMB_STATUS) { packet_type = "nmb_status"; opcode = 0; }
+ if (quest_type == NMB_QUERY ) { packet_type = "nmb_query"; opcode = 0; }
+ if (quest_type == NMB_REG ) { packet_type = "nmb_reg"; opcode = 5; }
+ if (quest_type == NMB_REL ) { packet_type = "nmb_rel"; opcode = 6; }
+
+ DEBUG(4,("initiating netbios packet: %s %s(%x) (bcast=%s) %s\n",
+ packet_type, name, name_type, BOOLSTR(bcast), inet_ntoa(to_ip)));
+
+ if (opcode == -1) return False;
+
+ 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 = opcode;
+ 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 = (quest_type==NMB_REG || quest_type==NMB_REL) ? 1 : 0;
+
+ make_nmb_name(&nmb->question.question_name,name,name_type,scope);
+
+ nmb->question.question_type = quest_type;
+ nmb->question.question_class = 0x1;
+
+ if (quest_type == NMB_REG || quest_type == NMB_REL)
+ {
+ nmb->additional = &additional_rec;
+ bzero((char *)nmb->additional,sizeof(*nmb->additional));
+
+ nmb->additional->rr_name = nmb->question.question_name;
+ nmb->additional->rr_type = nmb->question.question_type;
+ nmb->additional->rr_class = nmb->question.question_class;
+
+ nmb->additional->ttl = quest_type == NMB_REG ? lp_max_ttl() : 0;
+ nmb->additional->rdlength = 6;
+ nmb->additional->rdata[0] = nb_flags;
+ putip(&nmb->additional->rdata[2],(char *)&myip);
+ }
+
+ p.ip = to_ip;
+ p.port = NMB_PORT;
+ p.fd = fd;
+ p.timestamp = time(NULL);
+ p.packet_type = NMB_PACKET;
+
+ if (!send_packet(&p))
+ return(0);
+
+ return(name_trn_id);
+}
+
+
+/****************************************************************************
+ wrapper function to override a broadcast message and send it to the WINS
+ name server instead, if it exists. if wins is false, and there has been no
+ WINS server specified, the packet will NOT be sent.
+ ****************************************************************************/
+void queue_netbios_pkt_wins(int fd,int quest_type,enum cmd_type cmd,
+ char *name,int name_type,int nb_flags,
+ BOOL bcast,BOOL recurse,struct in_addr to_ip)
+{
+ if ((!lp_wins_support()) && (*lp_wins_server()))
+ {
+ /* samba is not a WINS server, and we are using a WINS server */
+ struct in_addr wins_ip;
+ wins_ip = *interpret_addr2(lp_wins_server());
+
+ if (!zero_ip(wins_ip))
+ {
+ bcast = False;
+ to_ip = wins_ip;
+ }
+ else
+ {
+ /* oops. smb.conf's wins server parameter MUST be a host_name
+ or an ip_address. */
+ DEBUG(0,("invalid smb.conf parameter 'wins server'\n"));
+ }
+ }
+
+ if (zero_ip(to_ip)) return;
+
+ queue_netbios_packet(fd, quest_type, cmd,
+ name, name_type, nb_flags,
+ bcast, recurse, to_ip);
+}
+
+/****************************************************************************
+ create a name query response record
+ **************************************************************************/
+static struct name_response_record *make_name_query_record(
+ enum cmd_type cmd,int id,int fd,
+ char *name,int type,
+ BOOL bcast,BOOL recurse,
+ struct in_addr ip)
+{
+ struct name_response_record *n;
+
+ if (!name || !name[0]) return NULL;
+
+ if (!(n = (struct name_response_record *)malloc(sizeof(*n))))
+ return(NULL);
+
+ n->response_id = id;
+ n->cmd_type = cmd;
+ n->fd = fd;
+ make_nmb_name(&n->name, name, type, scope);
+ n->bcast = bcast;
+ n->recurse = recurse;
+ n->to_ip = ip;
+ n->start_time = time(NULL);
+ n->num_msgs = 0;
+
+ return n;
+}
+
+
+/****************************************************************************
+ initiate a netbios name query to find someone's or someones' IP
+ this is intended to be used (not exclusively) for broadcasting to
+ master browsers (WORKGROUP(1d or 1b) or __MSBROWSE__(1)) to get
+ complete lists across a wide area network
+ ****************************************************************************/
+void queue_netbios_packet(int fd,int quest_type,enum cmd_type cmd,char *name,
+ int name_type,int nb_flags,BOOL bcast,BOOL recurse,
+ struct in_addr to_ip)
+{
+ uint16 id = initiate_netbios_packet(fd, quest_type, name, name_type,
+ nb_flags, bcast, recurse, to_ip);
+ struct name_response_record *n;
+
+ if (id == 0) return;
+
+ if ((n =
+ make_name_query_record(cmd,id,fd,name,name_type,bcast,recurse,to_ip)))
+ {
+ add_response_record(n);
+ }
+}
+
+
+/****************************************************************************
+ find a response in the name query response list
+ **************************************************************************/
+struct name_response_record *find_name_query(uint16 id)
+{
+ struct name_response_record *n;
+
+ for (n = nameresponselist; n; n = n->next)
+ {
+ if (n->response_id == id) {
+ return n;
+ }
+ }
+
+ return NULL;
+}
+
+
+/*******************************************************************
+ 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
+ ******************************************************************/
+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;
+}
+
+/*******************************************************************
+ run elements off the packet queue till its empty
+ ******************************************************************/
+void run_packet_queue()
+{
+ 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);
+ }
+}
+
+/****************************************************************************
+ listens for NMB or DGRAM packets, and queues them
+ ***************************************************************************/
+void listen_for_packets(BOOL run_election)
+{
+ fd_set fds;
+ int selrtn;
+ struct timeval timeout;
+
+ 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 = run_election ? 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) {
+#if 1
+ if (ip_equal(packet->ip,myip) &&
+ (packet->port == NMB_PORT || packet->port == DGRAM_PORT)) {
+ DEBUG(5,("discarding own packet from %s:%d\n",
+ inet_ntoa(packet->ip),packet->port));
+ free_packet(packet);
+ } else
+#endif
+ {
+ queue_packet(packet);
+ }
+ }
+ }
+
+ if (FD_ISSET(ClientDGRAM,&fds))
+ {
+ struct packet_struct *packet = read_packet(ClientDGRAM, DGRAM_PACKET);
+ if (packet) {
+#if 1
+ if (ip_equal(packet->ip,myip) &&
+ (packet->port == NMB_PORT || packet->port == DGRAM_PORT)) {
+ DEBUG(5,("discarding own packet from %s:%d\n",
+ inet_ntoa(packet->ip),packet->port));
+ free_packet(packet);
+ } else
+#endif
+ {
+ queue_packet(packet);
+ }
+ }
+ }
+}
+
+
+
+/****************************************************************************
+interpret a node status response. this is pretty hacked: we need two bits of
+info. a) the name of the workgroup b) the name of the server. it will also
+add all the names it finds into the namelist.
+****************************************************************************/
+BOOL interpret_node_status(char *p, struct nmb_name *name,int t,
+ char *serv_name, struct in_addr ip)
+{
+ int level = t==0x20 ? 4 : 0;
+ int numnames = CVAL(p,0);
+ BOOL found = False;
+
+ DEBUG(level,("received %d names\n",numnames));
+
+ p += 1;
+
+ if (serv_name) *serv_name = 0;
+
+ while (numnames--)
+ {
+ char qname[17];
+ int type;
+ fstring flags;
+ int nb_flags;
+
+ BOOL group = False;
+ BOOL add = False;
+
+ *flags = 0;
+
+ StrnCpy(qname,p,15);
+ type = CVAL(p,15);
+ nb_flags = p[16];
+
+ p += 18;
+
+ if (NAME_GROUP (nb_flags)) { strcat(flags,"<GROUP> "); group=True;}
+ if (NAME_BFLAG (nb_flags)) { strcat(flags,"B "); }
+ if (NAME_PFLAG (nb_flags)) { strcat(flags,"P "); }
+ if (NAME_MFLAG (nb_flags)) { strcat(flags,"M "); }
+ if (NAME__FLAG (nb_flags)) { strcat(flags,"_ "); }
+ if (NAME_DEREG (nb_flags)) { strcat(flags,"<DEREGISTERING> "); }
+ if (NAME_CONFLICT (nb_flags)) { strcat(flags,"<CONFLICT> "); add=True;}
+ if (NAME_ACTIVE (nb_flags)) { strcat(flags,"<ACTIVE> "); add=True; }
+ if (NAME_PERMANENT(nb_flags)) { strcat(flags,"<PERMANENT> "); add=True;}
+
+ /* might as well update our namelist while we're at it */
+ if (add)
+ {
+ struct in_addr nameip;
+ enum name_source src;
+
+ if (ip_equal(ip, myip))
+ {
+ nameip = ipzero;
+ src = SELF;
+ }
+ else
+ {
+ nameip = ip;
+ src = STATUS_QUERY;
+ }
+ add_netbios_entry(qname,type,nb_flags,2*60*60,src,nameip);
+ }
+
+ /* we want the server name */
+ if (serv_name && !*serv_name && !group && t == 0)
+ {
+ StrnCpy(serv_name,qname,15);
+ serv_name[15] = 0;
+ }
+
+ /* looking for a name and type? */
+ if (name && !found && (t == type))
+ {
+ /* take a guess at some of the name types we're going to ask for.
+ evaluate whether they are group names or no... */
+ if (((t == 0x1b || t == 0x1d ) && !group) ||
+ ((t == 0x20 || t == 0x1c || t == 0x1e) && group))
+ {
+ found = True;
+ make_nmb_name(name,qname,type,scope);
+ }
+ }
+
+ DEBUG(level,("\t%s(0x%x)\t%s\n",qname,type,flags));
+ }
+ DEBUG(level,("num_good_sends=%d num_good_receives=%d\n",
+ IVAL(p,20),IVAL(p,24)));
+ 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 = ClientDGRAM;
+ p.timestamp = time(NULL);
+ p.packet_type = DGRAM_PACKET;
+
+ return(send_packet(&p));
+}
+
+
diff --git a/source/nameserv.c b/source/nameserv.c
new file mode 100644
index 00000000000..b6bc8a4f060
--- /dev/null
+++ b/source/nameserv.c
@@ -0,0 +1,1045 @@
+/*
+ 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.
+
+ Revision History:
+
+ 14 jan 96: lkcl@pires.co.uk
+ added multiple workgroup domain master support
+
+*/
+
+#include "includes.h"
+#include "loadparm.h"
+#include "localnet.h"
+
+
+enum name_search { FIND_SELF, FIND_GLOBAL };
+
+extern int DEBUGLEVEL;
+
+extern pstring scope;
+extern BOOL CanRecurse;
+extern pstring myname;
+extern struct in_addr ipzero;
+
+/* netbios names database */
+struct name_record *namelist;
+
+#define GET_TTL(ttl) ((ttl)?MIN(ttl,lp_max_ttl()):lp_max_ttl())
+
+
+/****************************************************************************
+ 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;
+}
+
+/****************************************************************************
+ remove a name from the namelist. The pointer must be an element just
+ retrieved
+ **************************************************************************/
+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 domain database namelist
+ search can be:
+ FIND_SELF - look for names the samba server has added for itself
+ FIND_GLOBAL - the name can be anyone. first look on the client's
+ subnet, then the server's subnet, then all subnets.
+ **************************************************************************/
+static struct name_record *find_name_search(struct nmb_name *name, enum name_search search,
+ struct in_addr ip)
+{
+ struct name_record *ret;
+
+ /* any number of winpopup names can be added. must search by ip as well */
+ if (name->name_type != 0x3) ip = ipzero;
+
+ for (ret = namelist; ret; ret = ret->next)
+ {
+ if (name_equal(&ret->name,name))
+ {
+ /* self search: self names only */
+ if (search == FIND_SELF && ret->source != SELF) continue;
+
+ if (zero_ip(ip) || ip_equal(ip, ret->ip))
+ {
+ return ret;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+
+/****************************************************************************
+ dump a copy of the name table
+ **************************************************************************/
+void dump_names(void)
+{
+ struct name_record *n;
+ time_t t = time(NULL);
+
+ DEBUG(3,("Dump of local name table:\n"));
+
+ for (n = namelist; n; n = n->next)
+ {
+ DEBUG(3,("%s %s TTL=%d NBFLAGS=%2x\n",
+ namestr(&n->name),
+ inet_ntoa(n->ip),
+ n->death_time?n->death_time-t:0,
+ n->nb_flags));
+ }
+}
+
+
+/****************************************************************************
+ remove an entry from the name list
+ ****************************************************************************/
+void remove_netbios_name(char *name,int type, enum name_source source,
+ struct in_addr ip)
+{
+ struct nmb_name nn;
+ struct name_record *n;
+
+ make_nmb_name(&nn, name, type, scope);
+ n = find_name_search(&nn, FIND_GLOBAL, ip);
+
+ if (n && n->source == source) remove_name(n);
+}
+
+
+/****************************************************************************
+ add an entry to the name list
+ ****************************************************************************/
+struct name_record *add_netbios_entry(char *name, int type, int nb_flags, 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_search(&n->name, FIND_GLOBAL, ip)))
+ {
+ free(n);
+ n = n2;
+ }
+
+ if (ttl) n->death_time = time(NULL)+ttl*3;
+ n->ip = ip;
+ n->nb_flags = nb_flags;
+ n->source = source;
+
+ if (!n2) add_name(n);
+
+ DEBUG(3,("Added netbios name %s at %s ttl=%d nb_flags=%2x\n",
+ namestr(&n->name),inet_ntoa(ip),ttl,nb_flags));
+
+ return(n);
+}
+
+
+/****************************************************************************
+ remove an entry from the name list
+ ****************************************************************************/
+void remove_name_entry(char *name,int type)
+{
+ if (lp_wins_support())
+ {
+ /* we are a WINS server. */
+ remove_netbios_name(name,type,SELF,myip);
+ }
+ else
+ {
+ struct in_addr ip;
+ ip = ipzero;
+
+ queue_netbios_pkt_wins(ClientNMB,NMB_REL,NAME_RELEASE,
+ name, type, 0,
+ False, True, ip);
+ }
+}
+
+
+/****************************************************************************
+ add an entry to the name list
+ ****************************************************************************/
+void add_name_entry(char *name,int type,int nb_flags)
+{
+ /* always add our own entries */
+ add_netbios_entry(name,type,nb_flags,0,SELF,myip);
+
+ if (!lp_wins_support())
+ {
+ struct in_addr ip;
+ ip = ipzero;
+
+ queue_netbios_pkt_wins(ClientNMB,NMB_REG,NAME_REGISTER,
+ name, type, nb_flags,
+ False, True, ip);
+ }
+}
+
+
+/****************************************************************************
+ add the magic samba names, useful for finding samba servers
+ **************************************************************************/
+void add_my_names(void)
+{
+ struct in_addr ip;
+
+ ip = ipzero;
+
+ add_name_entry(myname,0x20,NB_ACTIVE);
+ add_name_entry(myname,0x03,NB_ACTIVE);
+ add_name_entry(myname,0x00,NB_ACTIVE);
+ add_name_entry(myname,0x1f,NB_ACTIVE);
+
+ add_netbios_entry("*",0x0,NB_ACTIVE,0,SELF,ip);
+ add_netbios_entry("__SAMBA__",0x20,NB_ACTIVE,0,SELF,ip);
+ add_netbios_entry("__SAMBA__",0x00,NB_ACTIVE,0,SELF,ip);
+
+ if (lp_wins_support()) {
+ add_netbios_entry(inet_ntoa(myip),0x01,NB_ACTIVE,0,SELF,ip); /* nt as? */
+ }
+}
+
+/*******************************************************************
+ refresh my own names
+ ******************************************************************/
+void refresh_my_names(time_t t)
+{
+ static time_t lasttime = 0;
+
+ if (t - lasttime < REFRESH_TIME)
+ return;
+ lasttime = t;
+
+ add_my_names();
+}
+
+/*******************************************************************
+ expires old names in the namelist
+ ******************************************************************/
+void expire_names(time_t t)
+{
+ struct name_record *n;
+ struct name_record *next;
+
+ /* 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;
+ }
+ }
+}
+
+
+/****************************************************************************
+response for a reg release received
+**************************************************************************/
+void response_name_release(struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ char *name = nmb->question.question_name.name;
+ int type = nmb->question.question_name.name_type;
+
+ DEBUG(4,("response name release received\n"));
+
+ if (nmb->header.rcode == 0 && nmb->answers->rdata)
+ {
+ struct in_addr found_ip;
+ putip((char*)&found_ip,&nmb->answers->rdata[2]);
+
+ if (ip_equal(found_ip, myip))
+ {
+ remove_netbios_name(name,type,SELF,found_ip);
+ }
+ }
+ else
+ {
+ DEBUG(1,("name registration for %s rejected!\n",
+ namestr(&nmb->question.question_name)));
+ }
+}
+
+
+/****************************************************************************
+reply to a name release
+****************************************************************************/
+void reply_name_release(struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct in_addr ip;
+ int rcode=0;
+ int opcode = nmb->header.opcode;
+ int nb_flags = nmb->additional->rdata[0];
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ struct name_record *n;
+ char rdata[6];
+
+ putip((char *)&ip,&nmb->additional->rdata[2]);
+
+ DEBUG(3,("Name release on name %s rcode=%d\n",
+ namestr(&nmb->question.question_name),rcode));
+
+ n = find_name_search(&nmb->question.question_name, FIND_GLOBAL, ip);
+
+ /* XXXX under what conditions should we reject the removal?? */
+ if (n && n->nb_flags == nb_flags && ip_equal(n->ip,ip))
+ {
+ /* success = True;
+ rcode = 6; */
+
+ remove_name(n);
+ n = NULL;
+ }
+
+ if (bcast) return;
+
+ /*if (success)*/
+ {
+ rdata[0] = nb_flags;
+ rdata[1] = 0;
+ putip(&rdata[2],(char *)&ip);
+ }
+
+ /* Send a NAME RELEASE RESPONSE */
+ reply_netbios_packet(p,nmb->header.name_trn_id,rcode,opcode,
+ &nmb->question.question_name,
+ nmb->question.question_type,
+ nmb->question.question_class,
+ 0,
+ rdata, 6 /*success ? 6 : 0*/);
+ /* XXXX reject packet never tested: cannot tell what to do */
+}
+
+
+/****************************************************************************
+response for a reg request received
+**************************************************************************/
+void response_name_reg(struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ char *name = nmb->question.question_name.name;
+ int type = nmb->question.question_name.name_type;
+
+ DEBUG(4,("response name registration received!\n"));
+
+ if (nmb->header.rcode == 0 && nmb->answers->rdata)
+ {
+ int nb_flags = nmb->answers->rdata[0];
+ struct in_addr found_ip;
+ int ttl = nmb->answers->ttl;
+ enum name_source source = REGISTER;
+
+ putip((char*)&found_ip,&nmb->answers->rdata[2]);
+
+ if (ip_equal(found_ip, myip)) source = SELF;
+
+ add_netbios_entry(name,type,nb_flags,ttl,source,found_ip);
+ }
+ else
+ {
+ DEBUG(1,("name registration for %s rejected!\n",
+ namestr(&nmb->question.question_name)));
+ }
+}
+
+
+/****************************************************************************
+reply to a reg request
+**************************************************************************/
+void reply_name_reg(struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+ char *qname = nmb->question.question_name.name;
+ int name_type = nmb->question.question_name.name_type;
+
+ BOOL bcast = nmb->header.nm_flags.bcast;
+
+ int ttl = GET_TTL(nmb->additional->ttl);
+ int nb_flags = nmb->additional->rdata[0];
+ BOOL group = (nb_flags&0x80);
+ int rcode = 0;
+ int opcode = nmb->header.opcode;
+ struct name_record *n = NULL;
+ int success = True;
+ char rdata[6];
+ struct in_addr ip, from_ip;
+
+ putip((char *)&from_ip,&nmb->additional->rdata[2]);
+ ip = from_ip;
+
+ DEBUG(3,("Name registration for name %s at %s rcode=%d\n",
+ namestr(question),inet_ntoa(ip),rcode));
+
+ if (group)
+ {
+ /* apparently we should return 255.255.255.255 for group queries
+ (email from MS) */
+ ip = *interpret_addr2("255.255.255.255");
+ }
+
+ /* see if the name already exists */
+ n = find_name_search(question, FIND_GLOBAL, from_ip);
+
+ if (n)
+ {
+ if (!group && !ip_equal(ip,n->ip) && question->name_type != 0x3)
+ {
+ if (n->source == SELF)
+ {
+ rcode = 6;
+ success = False;
+ }
+ 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 subnet/name database */
+ n = add_netbios_entry(qname,name_type,nb_flags,ttl,REGISTER,ip);
+ }
+
+ if (bcast) return;
+
+ update_from_reg(nmb->question.question_name.name,
+ nmb->question.question_name.name_type, from_ip);
+
+ /* XXXX don't know how to reject a name register: stick info in anyway
+ and guess that it doesn't matter if info is there! */
+ /*if (success)*/
+ {
+ rdata[0] = nb_flags;
+ rdata[1] = 0;
+ putip(&rdata[2],(char *)&ip);
+ }
+
+ /* Send a NAME REGISTRATION RESPONSE */
+ reply_netbios_packet(p,nmb->header.name_trn_id,rcode,opcode,
+ &nmb->question.question_name,
+ nmb->question.question_type,
+ nmb->question.question_class,
+ ttl,
+ rdata, 6 /*success ? 6 : 0*/);
+}
+
+
+/****************************************************************************
+reply to a name status query
+****************************************************************************/
+void reply_name_status(struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ char *qname = nmb->question.question_name.name;
+ int ques_type = nmb->question.question_name.name_type;
+ BOOL wildcard = (qname[0] == '*');
+ char rdata[MAX_DGRAM_SIZE];
+ char *countptr, *buf;
+ int count, names_added;
+ struct name_record *n;
+
+ DEBUG(3,("Name status for name %s %s\n",
+ namestr(&nmb->question.question_name), inet_ntoa(p->ip)));
+
+ /* find a name: if it's a wildcard, search the entire database.
+ if not, search for source SELF names only */
+ n = find_name_search(&nmb->question.question_name,
+ wildcard ? FIND_GLOBAL : FIND_SELF, p->ip);
+
+ if (!wildcard && (!n || n->source != SELF)) return;
+
+ for (count=0, n = namelist ; n; n = n->next)
+ {
+ int name_type = n->name.name_type;
+
+ if (n->source != SELF) continue;
+
+ if (name_type >= 0x1b && name_type <= 0x20 &&
+ ques_type >= 0x1b && ques_type <= 0x20)
+ {
+ if (!strequal(qname, n->name.name)) continue;
+ }
+
+ count++;
+ }
+
+ /* XXXX hack, we should calculate exactly how many will fit */
+ count = MIN(count,(sizeof(rdata) - 64) / 18);
+
+ countptr = buf = rdata;
+ buf += 1;
+
+ names_added = 0;
+
+ for (n = namelist ; n && count >= 0; n = n->next)
+ {
+ int name_type = n->name.name_type;
+
+ if (n->source != SELF) continue;
+
+ /* start with first bit of putting info in buffer: the name */
+
+ bzero(buf,18);
+ StrnCpy(buf,n->name.name,15);
+ strupper(buf);
+
+ /* now check if we want to exclude other workgroup names
+ from the response. if we don't exclude them, windows clients
+ get confused and will respond with an error for NET VIEW */
+
+ if (name_type >= 0x1b && name_type <= 0x20 &&
+ ques_type >= 0x1b && ques_type <= 0x20)
+ {
+ if (!strequal(qname, n->name.name)) continue;
+ }
+
+ /* carry on putting name info in buffer */
+
+ buf[15] = name_type;
+ buf[16] = n->nb_flags;
+
+ buf += 18;
+
+ count--;
+ names_added++;
+ }
+
+ if (count < 0)
+ {
+ DEBUG(3, (("too many names: missing a few!\n")));
+ }
+
+ SCVAL(countptr,0,names_added);
+
+ /* 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;
+
+ /* Send a POSITIVE NAME STATUS RESPONSE */
+ reply_netbios_packet(p,nmb->header.name_trn_id,0,0,
+ &nmb->question.question_name,
+ nmb->question.question_type,
+ nmb->question.question_class,
+ 0,
+ rdata,PTR_DIFF(buf,rdata));
+}
+
+
+/***************************************************************************
+reply to a name query
+****************************************************************************/
+struct name_record *search_for_name(struct nmb_name *question,
+ struct in_addr ip, int Time, int search)
+{
+ int name_type = question->name_type;
+ char *qname = question->name;
+ BOOL dns_type = name_type == 0x20 || name_type == 0;
+
+ struct name_record *n;
+
+ DEBUG(3,("Search for %s from %s - ", namestr(question), inet_ntoa(ip)));
+
+ /* first look up name in cache */
+ n = find_name_search(question,search,ip);
+
+ /* now try DNS lookup. */
+ if (!n)
+ {
+ struct in_addr dns_ip;
+ unsigned long a;
+
+ /* only do DNS lookups if the query is for type 0x20 or type 0x0 */
+ if (!dns_type)
+ {
+ DEBUG(3,("types 0x20 0x1b 0x0 only: name not found\n"));
+ return NULL;
+ }
+
+ /* look it up with DNS */
+ a = interpret_addr(qname);
+
+ putip((char *)&dns_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_netbios_entry(qname,name_type,NB_ACTIVE,60*60,DNSFAIL,dns_ip);
+ return NULL;
+ }
+
+ /* add it to our cache of names. give it 2 hours in the cache */
+ n = add_netbios_entry(qname,name_type,NB_ACTIVE,2*60*60,DNS,dns_ip);
+
+ /* failed to add it? yikes! */
+ if (!n) return NULL;
+ }
+
+ /* is our entry already dead? */
+ if (n->death_time)
+ {
+ if (n->death_time < Time) return False;
+ }
+
+ /* it may have been an earlier failure */
+ if (n->source == DNSFAIL)
+ {
+ DEBUG(3,("DNSFAIL\n"));
+ return NULL;
+ }
+
+ DEBUG(3,("OK %s\n",inet_ntoa(n->ip)));
+
+ return n;
+}
+
+
+/***************************************************************************
+reply to a name query.
+
+with broadcast name queries:
+
+ - only reply if the query is for one of YOUR names. all other machines on
+ the network will be doing the same thing (that is, only replying to a
+ broadcast query if they own it)
+ NOTE: broadcast name queries should only be sent out by a machine
+ if they HAVEN'T been configured to use WINS. this is generally bad news
+ in a wide area tcp/ip network and should be rectified by the systems
+ administrator. USE WINS! :-)
+ - the exception to this is if the query is for a Primary Domain Controller
+ type name (0x1b), in which case, a reply is sent.
+
+ - NEVER send a negative response to a broadcast query. no-one else will!
+
+with directed name queries:
+
+ - if you are the WINS server, you are expected to
+****************************************************************************/
+extern void reply_name_query(struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+ int name_type = question->name_type;
+ BOOL dns_type = name_type == 0x20 || name_type == 0;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ int ttl=0;
+ int rcode = 0;
+ int nb_flags = 0;
+ struct in_addr retip;
+ char rdata[6];
+
+ struct in_addr gp_ip = *interpret_addr2("255.255.255.255");
+ BOOL success = True;
+
+ struct name_record *n;
+ enum name_search search = dns_type || name_type == 0x1b ?
+ FIND_GLOBAL : FIND_SELF;
+
+ DEBUG(3,("Name query "));
+
+ if ((n = search_for_name(question,p->ip,p->timestamp, search)))
+ {
+ /* don't respond to broadcast queries unless the query is for
+ a name we own or it is for a Primary Domain Controller name */
+ if (bcast && n->source != SELF && name_type != 0x1b)
+ {
+ if (!lp_wins_proxy() || same_net(p->ip,n->ip,Netmask)) {
+ /* never reply with a negative response to broadcast queries */
+ return;
+ }
+ }
+
+ /* we will reply */
+ ttl = n->death_time - p->timestamp;
+ retip = n->ip;
+ nb_flags = n->nb_flags;
+ }
+ else
+ {
+ if (bcast) return; /* never reply negative response to bcasts */
+ success = False;
+ }
+
+ /* if asking for a group name (type 0x1e) return 255.255.255.255 */
+ if (ip_equal(retip, gp_ip) && name_type == 0x1e) retip = gp_ip;
+
+ /* 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;
+
+ if (success)
+ {
+ rcode = 0;
+ DEBUG(3,("OK %s\n",inet_ntoa(retip)));
+ }
+ else
+ {
+ rcode = 3;
+ DEBUG(3,("UNKNOWN\n"));
+ }
+
+ if (success)
+ {
+ rdata[0] = nb_flags;
+ rdata[1] = 0;
+ putip(&rdata[2],(char *)&retip);
+ }
+
+ reply_netbios_packet(p,nmb->header.name_trn_id,rcode,0,
+ &nmb->question.question_name,
+ nmb->question.question_type,
+ nmb->question.question_class,
+ ttl,
+ rdata, success ? 6 : 0);
+}
+
+
+/****************************************************************************
+response from a name query
+****************************************************************************/
+static void response_netbios_packet(struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+ char *qname = question->name;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ struct name_response_record *n;
+
+ if (nmb->answers == NULL)
+ {
+ DEBUG(3,("NMB packet response from %s (bcast=%s) - UNKNOWN\n",
+ inet_ntoa(p->ip),
+ BOOLSTR(bcast)));
+ return;
+ }
+
+ if (nmb->answers->rr_type == NMB_STATUS) {
+ DEBUG(3,("Name status "));
+ }
+
+ if (nmb->answers->rr_type == NMB_QUERY) {
+ DEBUG(3,("Name query "));
+ }
+
+ if (nmb->answers->rr_type == NMB_REG) {
+ DEBUG(3,("Name registration "));
+ }
+
+ if (nmb->answers->rr_type == NMB_REL) {
+ DEBUG(3,("Name release "));
+ }
+
+ DEBUG(3,("response for %s from %s (bcast=%s)\n",
+ namestr(&nmb->answers->rr_name),
+ inet_ntoa(p->ip),
+ BOOLSTR(bcast)));
+
+ if (!(n = find_name_query(nmb->header.name_trn_id))) {
+ DEBUG(3,("unknown response (received too late or from nmblookup?)\n"));
+ return;
+ }
+
+ n->num_msgs++; /* count number of responses received */
+
+ switch (n->cmd_type)
+ {
+ case MASTER_SERVER_CHECK : DEBUG(4,("MASTER_SVR_CHECK\n")); break;
+ case SERVER_CHECK : DEBUG(4,("SERVER_CHECK\n")); break;
+ case FIND_MASTER : DEBUG(4,("FIND_MASTER\n")); break;
+ case NAME_STATUS_MASTER_CHECK: DEBUG(4,("NAME_STAT_MST_CHK\n")); break;
+ case NAME_STATUS_CHECK : DEBUG(4,("NAME_STATUS_CHECK\n")); break;
+ case CHECK_MASTER : DEBUG(4,("CHECK_MASTER\n")); break;
+ case NAME_CONFIRM_QUERY : DEBUG(4,("NAME_CONFIRM_QUERY\n")); break;
+ default: break;
+ }
+ switch (n->cmd_type)
+ {
+ case MASTER_SERVER_CHECK:
+ case SERVER_CHECK:
+ case FIND_MASTER:
+ {
+ if (nmb->answers->rr_type == NMB_QUERY)
+ {
+ enum cmd_type cmd = (n->cmd_type == MASTER_SERVER_CHECK) ?
+ NAME_STATUS_MASTER_CHECK :
+ NAME_STATUS_CHECK;
+ if (n->num_msgs > 1 && !strequal(qname,n->name.name))
+ {
+ /* one subnet, one master browser per workgroup */
+ /* XXXX force an election? */
+ DEBUG(1,("more than one master browser replied!\n"));
+ }
+
+ /* initiate a name status check on the server that replied */
+ queue_netbios_packet(ClientNMB,NMB_STATUS, cmd,
+ nmb->answers->rr_name.name,
+ nmb->answers->rr_name.name_type,0,
+ False,False,n->to_ip);
+ }
+ else
+ {
+ DEBUG(1,("Name query reply has wrong answer rr_type\n"));
+ }
+ break;
+ }
+
+ case NAME_STATUS_MASTER_CHECK:
+ case NAME_STATUS_CHECK:
+ {
+ if (nmb->answers->rr_type == NMB_STATUS)
+ {
+ /* NMB_STATUS arrives: contains the workgroup name
+ and server name we require */
+ struct nmb_name name;
+ fstring serv_name;
+
+ if (interpret_node_status(nmb->answers->rdata,
+ &name,0x1d,serv_name,n->to_ip))
+ {
+ if (*serv_name)
+ {
+ sync_server(n->cmd_type,serv_name,
+ name.name,name.name_type,
+ n->to_ip);
+ }
+ }
+ else
+ {
+ DEBUG(1,("No 0x1d name type in interpret_node_status()\n"));
+ }
+ }
+ else
+ {
+ DEBUG(1,("Name status reply has wrong answer rr_type\n"));
+ }
+ break;
+ }
+
+ case CHECK_MASTER:
+ {
+ /* no action required here. it's when NO responses are received
+ that we need to do something (see expire_name_query_entries) */
+
+ DEBUG(4, ("Master browser exists for %s at %s\n",
+ namestr(&n->name),
+ inet_ntoa(n->to_ip)));
+ if (n->num_msgs > 1)
+ {
+ DEBUG(1,("more than one master browser!\n"));
+ }
+ if (nmb->answers->rr_type != NMB_QUERY)
+ {
+ DEBUG(1,("Name query reply has wrong answer rr_type\n"));
+ }
+ break;
+ }
+ case NAME_CONFIRM_QUERY:
+ {
+ DEBUG(4, ("Name query at WINS server: %s at %s - ",
+ namestr(&n->name),
+ inet_ntoa(n->to_ip)));
+ if (nmb->header.rcode == 0 && nmb->answers->rdata)
+ {
+ int nb_flags = nmb->answers->rdata[0];
+ struct in_addr found_ip;
+ putip((char*)&found_ip,&nmb->answers->rdata[2]);
+
+ DEBUG(4, (" OK: %s\n", inet_ntoa(found_ip)));
+ add_netbios_entry(nmb->answers->rr_name.name,
+ nmb->answers->rr_name.name_type,
+ nb_flags,GET_TTL(0),STATUS_QUERY,found_ip);
+ }
+ else
+ {
+ DEBUG(4, (" NEGATIVE RESPONSE\n"));
+ }
+
+ break;
+ }
+ default:
+ {
+ DEBUG(0,("unknown command received in response_netbios_packet\n"));
+ break;
+ }
+ }
+}
+
+
+/****************************************************************************
+ process a nmb packet
+ ****************************************************************************/
+void process_nmb(struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+
+ debug_nmb_packet(p);
+
+ switch (nmb->header.opcode)
+ {
+ case 5:
+ case 8:
+ case 9:
+ {
+ if (nmb->header.qdcount==0 || nmb->header.arcount==0) break;
+ if (nmb->header.response)
+ response_name_reg(p);
+ else
+ reply_name_reg(p);
+ break;
+ }
+
+ case 0:
+ {
+ if (nmb->header.response)
+ {
+ switch (nmb->question.question_type)
+ {
+ case 0x0:
+ {
+ response_netbios_packet(p);
+ break;
+ }
+ }
+ return;
+ }
+ else if (nmb->header.qdcount>0)
+ {
+ switch (nmb->question.question_type)
+ {
+ case NMB_QUERY:
+ {
+ reply_name_query(p);
+ break;
+ }
+ case NMB_STATUS:
+ {
+ reply_name_status(p);
+ break;
+ }
+ }
+ return;
+ }
+ break;
+ }
+
+ case 6:
+ {
+ if (nmb->header.qdcount==0 || nmb->header.arcount==0)
+ {
+ DEBUG(2,("netbios release packet rejected\n"));
+ break;
+ }
+
+ if (nmb->header.response)
+ response_name_release(p);
+ else
+ reply_name_release(p);
+ break;
+ }
+ }
+
+}
+
diff --git a/source/namework.c b/source/namework.c
new file mode 100644
index 00000000000..9697be23ef6
--- /dev/null
+++ b/source/namework.c
@@ -0,0 +1,1017 @@
+/*
+ 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.
+
+ Revision History:
+
+ 14 jan 96: lkcl@pires.co.uk
+ added multiple workgroup domain master support
+
+*/
+
+#include "includes.h"
+#include "loadparm.h"
+#include "localnet.h"
+
+#define TEST_CODE /* want to debug unknown browse packets */
+
+extern int DEBUGLEVEL;
+extern pstring scope;
+extern BOOL CanRecurse;
+
+extern struct in_addr myip;
+extern struct in_addr bcast_ip;
+extern struct in_addr Netmask;
+
+extern pstring myname;
+
+extern int ClientNMB;
+extern int ClientDGRAM;
+
+extern int workgroup_count; /* total number of workgroups we know about */
+
+/* this is our browse cache database */
+extern struct browse_cache_record *browserlist;
+
+/* this is our domain/workgroup/server database */
+extern struct domain_record *domainlist;
+
+/* machine comment for host announcements */
+extern pstring ServerComment;
+
+extern int updatecount;
+
+/* what server type are we currently */
+#define DFLT_SERVER_TYPE (SV_TYPE_WORKSTATION | SV_TYPE_SERVER | \
+ SV_TYPE_TIME_SOURCE | SV_TYPE_SERVER_UNIX |\
+ SV_TYPE_PRINTQ_SERVER | SV_TYPE_POTENTIAL_BROWSER)
+
+/* backup request types: which servers are to be included */
+#define MASTER_TYPE (SV_TYPE_MASTER_BROWSER)
+#define DOMCTL_TYPE (SV_TYPE_DOMAIN_CTRL )
+
+extern time_t StartupTime;
+
+#define AM_MASTER(work) (work->ServerType & SV_TYPE_MASTER_BROWSER)
+#define AM_BACKUP(work) (work->ServerType & SV_TYPE_BACKUP_BROWSER)
+
+#define MSBROWSE "\001\002__MSBROWSE__\002"
+#define BROWSE_MAILSLOT "\\MAILSLOT\\BROWSE"
+
+#define GET_TTL(ttl) ((ttl)?MIN(ttl,lp_max_ttl()):lp_max_ttl())
+
+
+/****************************************************************************
+tell a server to become a backup browser
+state - 0x01 become backup instead of master
+ - 0x02 remove all entries in browse list and become non-master
+ - 0x04 stop master browser service altogether. NT ignores this
+**************************************************************************/
+void reset_server(char *name, int state, struct in_addr ip)
+{
+ char outbuf[20];
+ char *p;
+
+ bzero(outbuf,sizeof(outbuf));
+ p = outbuf;
+
+ CVAL(p,0) = ANN_ResetBrowserState;
+ CVAL(p,2) = state;
+ p += 2;
+
+ DEBUG(2,("sending reset to %s %s of state %d\n",
+ name,inet_ntoa(ip),state));
+
+ send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
+ myname,name,0x20,0x1d,ip,myip);
+}
+
+
+/****************************************************************************
+tell a server to become a backup browser
+**************************************************************************/
+void tell_become_backup(void)
+{
+ struct domain_record *d;
+ for (d = domainlist; d; d = d->next)
+ {
+ struct work_record *work;
+ for (work = d->workgrouplist; work; work = work->next)
+ {
+ struct server_record *s;
+ int num_servers = 0;
+ int num_backups = 0;
+
+ for (s = work->serverlist; s; s = s->next)
+ {
+ if (s->serv.type & SV_TYPE_DOMAIN_ENUM) continue;
+
+ num_servers++;
+
+ if (strequal(myname, s->serv.name)) continue;
+
+ if (s->serv.type & SV_TYPE_BACKUP_BROWSER) {
+ num_backups++;
+ continue;
+ }
+
+ if (s->serv.type & SV_TYPE_MASTER_BROWSER) continue;
+
+ if (!(s->serv.type & SV_TYPE_POTENTIAL_BROWSER)) continue;
+
+ DEBUG(3,("num servers: %d num backups: %d\n",
+ num_servers, num_backups));
+
+ /* make first server a backup server. thereafter make every
+ tenth server a backup server */
+ if (num_backups != 0 && (num_servers+9) / num_backups > 10)
+ {
+ continue;
+ }
+
+ DEBUG(2,("sending become backup to %s %s for %s\n",
+ s->serv.name, inet_ntoa(d->bcast_ip),
+ work->work_group));
+
+ /* type 11 request from MYNAME(20) to WG(1e) for SERVER */
+ do_announce_request(s->serv.name, work->work_group,
+ ANN_BecomeBackup, 0x20, 0x1e, d->bcast_ip);
+ }
+ }
+ }
+}
+
+/****************************************************************************
+find a server responsible for a workgroup, and sync browse lists
+**************************************************************************/
+static BOOL sync_browse_entry(struct browse_cache_record *b)
+{
+ struct domain_record *d;
+ struct work_record *work;
+ /*
+ if (!strequal(serv_name, b->name))
+ {
+ DEBUG(0, ("browser's netbios name (%s) does not match %s (%s)",
+ b->name, inet_ntoa(b->ip), serv_name));
+ }
+ */
+ if (!(d = find_domain(b->ip))) return False;
+ if (!(work = find_workgroupstruct(d, b->group, False))) return False;
+
+ if (AM_MASTER(work)) {
+ /* only try to sync browse lists if we are the master, otherwise
+ the net could get a little bit too busy */
+ sync_browse_lists(work,b->name,0x20,b->ip);
+ }
+ b->synced = True;
+
+ return True;
+}
+
+
+/****************************************************************************
+search through browser list for an entry to sync with
+**************************************************************************/
+void do_browser_lists(void)
+{
+ struct browse_cache_record *b;
+ static time_t last = 0;
+ time_t t = time(NULL);
+
+ if (t-last < 4) return; /* don't do too many of these at once! */
+
+ last = t;
+
+ /* pick any entry in the list, preferably one whose time is up */
+ for (b = browserlist; b && b->next; b = b->next)
+ {
+ if (b->sync_time < t && b->synced == False) break;
+ }
+
+ if (!b || b->synced || sync_browse_entry(b))
+ {
+ /* leave entries (even ones already sync'd) for up to a minute.
+ this stops them getting re-sync'd too often */
+ expire_browse_cache(t - 60);
+ }
+}
+
+
+/****************************************************************************
+find a server responsible for a workgroup, and sync browse lists
+control ends up back here via response_name_query.
+**************************************************************************/
+void sync_server(enum cmd_type cmd, char *serv_name, char *work_name,
+ int name_type,
+ struct in_addr ip)
+{
+ add_browser_entry(serv_name, name_type, work_name, 0, ip);
+
+ if (cmd == MASTER_SERVER_CHECK)
+ {
+ /* announce ourselves as a master browser to serv_name */
+ do_announce_request(myname, serv_name, ANN_MasterAnnouncement,
+ 0x20, 0, ip);
+ }
+}
+
+
+/****************************************************************************
+update workgroup database from a name registration
+**************************************************************************/
+void update_from_reg(char *name, int type, struct in_addr ip)
+{
+ /* default server type: minimum guess at requirement XXXX */
+
+ DEBUG(3,("update from registration: host %s ip %s type %0x\n",
+ name, inet_ntoa(ip), type));
+
+ /* workgroup types, but not a chat type */
+ if (type >= 0x1b && type <= 0x1e)
+ {
+ struct work_record *work;
+ struct domain_record *d;
+
+ if (!(d = find_domain(ip))) return;
+ if (!(work = find_workgroupstruct(d, name, False))) return;
+
+ /* request the server to announce if on our subnet */
+ if (ip_equal(bcast_ip, d->bcast_ip)) announce_request(work, ip);
+
+ /* domain master type or master browser type */
+ if (type == 0x1b || type == 0x1d)
+ {
+ struct hostent *hp = gethostbyaddr((char*)&ip, sizeof(ip), AF_INET);
+ if (hp) {
+ /* gethostbyaddr name may not match netbios name but who cares */
+ add_browser_entry(hp->h_name, type, work->work_group, 120, ip);
+ }
+ }
+ }
+}
+
+
+/****************************************************************************
+ add the default workgroup into my domain
+ **************************************************************************/
+void add_my_domains(void)
+{
+ /* add or find domain on our local subnet, in the default workgroup */
+
+ if (*lp_workgroup() != '*')
+ {
+ add_domain_entry(bcast_ip,Netmask,lp_workgroup(), True);
+ }
+}
+
+
+/****************************************************************************
+ send a backup list response.
+ **************************************************************************/
+static void send_backup_list(char *work_name, struct nmb_name *src_name,
+ int info_count, int token, int info,
+ int name_type, struct in_addr ip)
+{
+ struct domain_record *d;
+ char outbuf[1024];
+ char *p, *countptr, *nameptr;
+ int count = 0;
+ int i, j;
+ char *theirname = src_name->name;
+
+ DEBUG(3,("sending backup list of %s to %s: %s(%x) %s(%x)\n",
+ work_name, inet_ntoa(ip),
+ myname,0x20,theirname,0x0));
+
+ if (name_type == 0x1d)
+ {
+ DEBUG(4,("master browsers: "));
+ }
+ else if (name_type == 0x1b)
+ {
+ DEBUG(4,("domain controllers: "));
+ }
+ else
+ {
+ DEBUG(0,("backup request for unknown type %0x\n", name_type));
+ return;
+ }
+
+ bzero(outbuf,sizeof(outbuf));
+ p = outbuf;
+
+ CVAL(p,0) = 10; /* backup list response */
+ p++;
+
+ countptr = p; /* count pointer */
+
+ SSVAL(p,1,token); /* sender's workgroup index representation */
+ SSVAL(p,3,info); /* XXXX clueless: info, usually zero */
+ p += 5;
+
+ nameptr = p;
+
+ for (d = domainlist; d; d = d->next)
+ {
+ struct work_record *work;
+
+ for (work = d->workgrouplist; work; work = work->next)
+ {
+ struct server_record *s;
+
+ if (!strequal(work->work_group, work_name)) continue;
+
+ for (s = work->serverlist; s; s = s->next)
+ {
+ BOOL found = False;
+ char *n;
+
+ if (s->serv.type & SV_TYPE_DOMAIN_ENUM) continue;
+
+ for (n = nameptr; n < p; n = skip_string(n, 1))
+ {
+ if (strequal(n, s->serv.name)) found = True;
+ }
+
+ if (found) continue; /* exclude names already added */
+
+ /* workgroup request: include all backup browsers in the list */
+ /* domain request: include all domain members in the list */
+
+ if ((name_type == 0x1d && (s->serv.type & MASTER_TYPE)) ||
+ (name_type == 0x1b && (s->serv.type & DOMCTL_TYPE)))
+ {
+ DEBUG(4, ("%s ", s->serv.name));
+
+ count++;
+ strcpy(p,s->serv.name);
+ strupper(p);
+ p = skip_string(p,1);
+ }
+ }
+ }
+ }
+
+ if (count == 0)
+ {
+ DEBUG(4, ("none\n"));
+ return;
+ }
+ else
+ {
+ DEBUG(4, (" - count %d\n", count));
+ }
+
+ CVAL(countptr,0) = count; /* total number of backup browsers found */
+
+ {
+ int len = PTR_DIFF(p, outbuf);
+
+ for (i = 0; i < len; i+= 16)
+ {
+ DEBUG(4, ("%3x char ", i));
+
+ for (j = 0; j < 16; j++)
+ {
+ unsigned char x = outbuf[i+j];
+ if (x < 32 || x > 127) x = '.';
+
+ if (i+j >= len) break;
+ DEBUG(4, ("%c", x));
+ }
+
+ DEBUG(4, (" hex ", i));
+
+ for (j = 0; j < 16; j++)
+ {
+ if (i+j >= len) break;
+ DEBUG(4, (" %02x", outbuf[i+j]));
+ }
+
+ DEBUG(4, ("\n"));
+ }
+
+ }
+ send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
+ myname,theirname,0x20,0,ip,myip);
+}
+
+
+/*******************************************************************
+ same context: scope. should check name_type as well, and makes sure
+ we don't process messages from ourselves
+ ******************************************************************/
+BOOL same_context(struct dgram_packet *dgram)
+{
+ if (!strequal(dgram->dest_name .scope,scope )) return(True);
+ if ( strequal(dgram->source_name.name ,myname)) return(True);
+
+ return(False);
+}
+
+
+/*******************************************************************
+ am I listening on a name. XXXX check the type of name as well.
+ ******************************************************************/
+BOOL listening_name(struct work_record *work, struct nmb_name *n)
+{
+ if (strequal(n->name,myname) ||
+ strequal(n->name,work->work_group) ||
+ 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;
+ struct in_addr ip = dgram->header.source_ip;
+ struct domain_record *d = find_domain(ip);
+
+ 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;
+ struct work_record *work;
+ char *work_name;
+ char *serv_name = dgram->source_name.name;
+
+ comment[43] = 0;
+
+ DEBUG(4,("Announce(%d) %s(%x)",command,name,name[15]));
+ DEBUG(4,("%s count=%d ttl=%d OS=(%d,%d) type=%08x comment=%s\n",
+ namestr(&dgram->dest_name),update_count,ttl,osmajor,osminor,
+ servertype,comment));
+
+ name[15] = 0;
+
+ if (dgram->dest_name.name_type == 0 && command == ANN_HostAnnouncement)
+ {
+ DEBUG(2,("Announce to nametype(0) not supported yet\n"));
+ return;
+ }
+
+ if (command == ANN_DomainAnnouncement &&
+ ((!strequal(dgram->dest_name.name, MSBROWSE)) ||
+ dgram->dest_name.name_type != 0x1))
+ {
+ DEBUG(0,("Announce(%d) from %s should be __MSBROWSE__(1) not %s\n",
+ command, inet_ntoa(ip), namestr(&dgram->dest_name)));
+ return;
+ }
+
+ if (same_context(dgram)) return;
+
+ if (command == ANN_DomainAnnouncement) {
+ work_name = name;
+ } else {
+ work_name = dgram->dest_name.name;
+ }
+
+ if (!(work = find_workgroupstruct(d, work_name, False))) return;
+
+ DEBUG(4, ("workgroup %s on %s\n", work->work_group, serv_name));
+
+ ttl = GET_TTL(ttl);
+
+ /* add them to our browse list */
+ add_server_entry(d,work,name,servertype,ttl,comment,True);
+
+#if 0
+ /* the tell become backup code is broken, no great harm is done by
+ disabling it */
+ tell_become_backup();
+#endif
+
+ /* get their browse list from them and add it to ours. */
+ add_browser_entry(serv_name,dgram->dest_name.name_type,
+ work->work_group,30,ip);
+}
+
+/*******************************************************************
+ process a master announcement frame
+ ******************************************************************/
+static void process_master_announce(struct packet_struct *p,char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ struct in_addr ip = dgram->header.source_ip;
+ struct domain_record *d = find_domain(ip);
+ struct domain_record *mydomain = find_domain(bcast_ip);
+ char *name = buf;
+ struct work_record *work;
+ name[15] = 0;
+
+ DEBUG(3,("Master Announce from %s (%s)\n",name,inet_ntoa(ip)));
+
+ if (same_context(dgram)) return;
+
+ if (!d || !mydomain) return;
+
+ if (!lp_domain_master()) return;
+
+ for (work = mydomain->workgrouplist; work; work = work->next)
+ {
+ if (AM_MASTER(work))
+ {
+ /* merge browse lists with them */
+ add_browser_entry(name,0x1b, work->work_group,30,ip);
+ }
+ }
+}
+
+/*******************************************************************
+ process a receive backup list request
+
+ we receive a list of servers, and we attempt to locate them all on
+ our local subnet, and sync browse lists with them on the workgroup
+ they are said to be in.
+ ******************************************************************/
+static void process_rcv_backup_list(struct packet_struct *p,char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ struct in_addr ip = dgram->header.source_ip;
+ int count = CVAL(buf,0);
+ int Index = IVAL(buf,1); /* caller's index representing workgroup */
+ char *buf1;
+
+ DEBUG(3,("Receive Backup ack for %s from %s total=%d index=%d\n",
+ namestr(&dgram->dest_name), inet_ntoa(ip),
+ count, Index));
+
+ if (same_context(dgram)) return;
+
+ if (count <= 0) return;
+
+ /* go through the list of servers attempting to sync browse lists */
+ for (buf1 = buf+5; *buf1 && count; buf1 = skip_string(buf1, 1), --count)
+ {
+ struct in_addr back_ip;
+ struct domain_record *d;
+
+ DEBUG(4,("Searching for backup browser %s at %s...\n",
+ buf1, inet_ntoa(ip)));
+
+ /* XXXX assume name is a DNS name NOT a netbios name. a more complete
+ approach is to use reply_name_query functionality to find the name */
+ back_ip = *interpret_addr2(buf1);
+
+ if (zero_ip(back_ip))
+ {
+ DEBUG(4,("Failed to find backup browser server using DNS\n"));
+ continue;
+ }
+
+ DEBUG(4,("Found browser server at %s\n", inet_ntoa(back_ip)));
+
+ if ((d = find_domain(back_ip)))
+ {
+ struct domain_record *d1;
+ for (d1 = domainlist; d1; d1 = d1->next)
+ {
+ struct work_record *work;
+ for (work = d1->workgrouplist; work; work = work->next)
+ {
+ if (work->token == Index)
+ {
+ queue_netbios_packet(ClientNMB,NMB_QUERY,SERVER_CHECK,
+ work->work_group,0x1d,0,
+ False,False,back_ip);
+ return;
+ }
+ }
+ }
+ }
+ }
+}
+
+/*******************************************************************
+ process a send 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
+ own. For larger nets we'll have to add backups and send "become
+ backup" requests occasionally.
+ ******************************************************************/
+static void process_send_backup_list(struct packet_struct *p,char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ struct in_addr ip = dgram->header.source_ip;
+ struct domain_record *d; /* = find_domain(ip); */
+ struct work_record *work;
+
+ int count = CVAL(buf,0);
+ int token = SVAL(buf,1); /* sender's key index for the workgroup? */
+ int info = SVAL(buf,3); /* XXXX don't know: some sort of info */
+ int name_type = dgram->dest_name.name_type;
+
+ if (same_context(dgram)) return;
+
+ if (count <= 0) return;
+
+ if (name_type != 0x1b && name_type != 0x1d) {
+ DEBUG(0,("backup request to wrong type %d from %s\n",
+ name_type,inet_ntoa(ip)));
+ return;
+ }
+
+ for (d = domainlist; d; d = d->next)
+ {
+ for (work = d->workgrouplist; work; work = work->next)
+ {
+ if (strequal(work->work_group, dgram->dest_name.name))
+ {
+ DEBUG(2,("sending backup list to %s %s count=%d\n",
+ namestr(&dgram->dest_name),inet_ntoa(ip),count));
+
+ send_backup_list(work->work_group,&dgram->source_name,
+ count,token,info,name_type,ip);
+ return;
+ }
+ }
+ }
+}
+
+
+/*******************************************************************
+ process a reset browser state
+
+ diagnostic packet:
+ 0x1 - stop being a master browser
+ 0x2 - discard browse lists, stop being a master browser, try again.
+ 0x4 - stop being a master browser forever. no way. ain't gonna.
+
+ ******************************************************************/
+static void process_reset_browser(struct packet_struct *p,char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ int state = CVAL(buf,0);
+
+ DEBUG(1,("received diagnostic browser reset request to %s state=0x%X\n",
+ namestr(&dgram->dest_name), state));
+
+ /* stop being a master but still deal with being a backup browser */
+ if (state & 0x1)
+ {
+ struct domain_record *d;
+ for (d = domainlist; d; d = d->next)
+ {
+ struct work_record *work;
+ for (work = d->workgrouplist; work; work = work->next)
+ {
+ if (AM_MASTER(work))
+ {
+ become_nonmaster(d,work);
+ }
+ }
+ }
+ }
+
+ /* totally delete all servers and start afresh */
+ if (state & 0x2)
+ {
+ struct domain_record *d;
+ for (d = domainlist; d; d = d->next)
+ {
+ struct work_record *work;
+ for (work=d->workgrouplist;work;work=remove_workgroup(d,work));
+ }
+ add_my_domains();
+ }
+
+ /* stop browsing altogether. i don't think this is a good idea! */
+ if (state & 0x4)
+ {
+ DEBUG(1,("ignoring request to stop being a browser. sorry!\n"));
+ }
+}
+
+
+/*******************************************************************
+ 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;
+ struct work_record *work;
+ struct in_addr ip = dgram->header.source_ip;
+ struct domain_record *d = find_domain(ip);
+ int token = CVAL(buf,0);
+ char *name = buf+1;
+
+ name[15] = 0;
+
+ DEBUG(3,("Announce request from %s to %s token=0x%X\n",
+ name,namestr(&dgram->dest_name), token));
+
+ if (strequal(dgram->source_name.name,myname)) return;
+
+ if (!d) return;
+
+ if (!ip_equal(bcast_ip, d->bcast_ip)) return;
+
+ for (work = d->workgrouplist; work; work = work->next)
+ {
+ if (strequal(dgram->dest_name.name,work->work_group))
+ {
+ work->needannounce = True;
+ }
+ }
+}
+
+
+/****************************************************************************
+ process a domain logon packet
+ **************************************************************************/
+void process_logon_packet(struct packet_struct *p,char *buf,int len)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ struct in_addr ip = dgram->header.source_ip;
+ struct domain_record *d = find_domain(ip);
+ char *logname,*q;
+ char *reply_name;
+ BOOL add_slashes = False;
+ pstring outbuf;
+ int code,reply_code;
+ struct work_record *work;
+
+ if (!d) return;
+
+ if (!(work = find_workgroupstruct(d,dgram->dest_name.name, False)))
+ return;
+
+ if (!lp_domain_logons()) {
+ DEBUG(3,("No domain logons\n"));
+ return;
+ }
+ if (!listening_name(work, &dgram->dest_name))
+ {
+ DEBUG(4,("Not listening to that domain\n"));
+ return;
+ }
+
+ code = SVAL(buf,0);
+ switch (code) {
+ case 0:
+ {
+ char *machine = buf+2;
+ char *user = skip_string(machine,1);
+ logname = skip_string(user,1);
+ reply_code = 6;
+ reply_name = myname;
+ add_slashes = True;
+ 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);
+ reply_code = 7;
+ reply_name = lp_domain_controller();
+ if (!*reply_name) {
+ DEBUG(3,("No domain controller configured\n"));
+ return;
+ }
+ DEBUG(3,("GETDC request from %s(%s)\n",
+ machine,inet_ntoa(p->ip)));
+ }
+ break;
+ default:
+ DEBUG(3,("Unknown domain request %d\n",code));
+ return;
+ }
+
+ bzero(outbuf,sizeof(outbuf));
+ q = outbuf;
+ SSVAL(q,0,reply_code);
+ q += 2;
+ if (add_slashes) {
+ strcpy(q,"\\\\");
+ q += 2;
+ }
+ StrnCpy(q,reply_name,16);
+ strupper(q);
+ q = skip_string(q,1);
+ SSVAL(q,0,0xFFFF);
+ q += 2;
+
+ send_mailslot_reply(logname,ClientDGRAM,outbuf,PTR_DIFF(q,outbuf),
+ myname,&dgram->source_name.name[0],0x20,0,p->ip,myip);
+ }
+
+
+/****************************************************************************
+depending on what announce has been made, we are only going to
+accept certain types of name announce. XXXX untested code
+
+check listening name type
+****************************************************************************/
+BOOL listening_type(struct packet_struct *p, int command)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ int type = dgram->dest_name.name_type;
+
+ switch (command)
+ {
+ case ANN_HostAnnouncement:
+ {
+ if (type != 0x0 || type != 0x20) return (False);
+ break;
+ }
+
+ case ANN_AnnouncementRequest:
+ {
+ return (True);
+ break;
+ }
+
+ case ANN_Election:
+ {
+ return (True);
+ break;
+ }
+
+ case ANN_GetBackupListReq:
+ {
+ return (True);
+ break;
+ }
+
+ case ANN_GetBackupListResp:
+ {
+ return (True);
+ break;
+ }
+
+ case ANN_DomainAnnouncement:
+ {
+ if (type != 0x1b || type != 0x1c) return (False);
+ break;
+ }
+
+ case ANN_MasterAnnouncement:
+ {
+ if (type != 0x1d) return (False);
+ break;
+ }
+
+ case ANN_LocalMasterAnnouncement:
+ {
+ if (type != 0x1c || type != 0x1d) return (False);
+ break;
+ }
+ }
+ return (True); /* we're not dealing with unknown packet types */
+}
+
+
+/****************************************************************************
+process a browse frame
+****************************************************************************/
+void process_browse_packet(struct packet_struct *p,char *buf,int len)
+{
+ int command = CVAL(buf,0);
+ switch (command)
+ {
+ case ANN_HostAnnouncement:
+ case ANN_DomainAnnouncement:
+ case ANN_LocalMasterAnnouncement:
+ {
+ process_announce(p,command,buf+1);
+ break;
+ }
+
+ case ANN_AnnouncementRequest:
+ {
+ process_announce_request(p,buf+1);
+ break;
+ }
+
+ case ANN_Election:
+ {
+ process_election(p,buf+1);
+ break;
+ }
+
+ case ANN_GetBackupListReq:
+ {
+ process_send_backup_list(p,buf+1);
+ break;
+ }
+
+ case ANN_GetBackupListResp:
+ {
+ process_rcv_backup_list(p, buf+1);
+ break;
+ }
+
+ case ANN_ResetBrowserState:
+ {
+ process_reset_browser(p, buf+1);
+ break;
+ }
+
+ case ANN_MasterAnnouncement:
+ {
+ process_master_announce(p,buf+1);
+ break;
+ }
+
+ default:
+ {
+ struct dgram_packet *dgram = &p->packet.dgram;
+ DEBUG(4,("ignoring browse packet %d from %s %s to %s\n",
+ command, namestr(&dgram->source_name),
+ inet_ntoa(p->ip), namestr(&dgram->dest_name)));
+ }
+ }
+}
+
+
+/****************************************************************************
+process udp 138 datagrams
+****************************************************************************/
+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(4,("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);
+ }
+}
+
diff --git a/source/nmbd/nmbd.c b/source/nmbd/nmbd.c
new file mode 100644
index 00000000000..a977667c2e2
--- /dev/null
+++ b/source/nmbd/nmbd.c
@@ -0,0 +1,610 @@
+/*
+ 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.
+
+ Revision History:
+
+ 14 jan 96: lkcl@pires.co.uk
+ added multiple workgroup domain master support
+
+*/
+
+#include "includes.h"
+#include "loadparm.h"
+#include "localnet.h"
+
+extern int DEBUGLEVEL;
+
+extern pstring debugf;
+pstring servicesf = CONFIGFILE;
+
+extern pstring scope;
+
+int ClientNMB = -1;
+int ClientDGRAM = -1;
+
+extern pstring myhostname;
+static pstring host_file;
+extern pstring myname;
+
+/* are we running as a daemon ? */
+static BOOL is_daemon = False;
+
+/* machine comment for host announcements */
+pstring ServerComment="";
+
+static BOOL got_bcast = False;
+static BOOL got_myip = False;
+static BOOL got_nmask = False;
+
+/* what server type are we currently */
+
+time_t StartupTime =0;
+
+struct in_addr ipzero;
+
+
+/****************************************************************************
+catch a sighup
+****************************************************************************/
+static int sig_hup(void)
+{
+ 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(void)
+{
+ 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)
+{
+#if DUMP_CORE
+ dump_core();
+#endif
+}
+
+/*******************************************************************
+ expire old names from the namelist and server list
+ ******************************************************************/
+static void expire_names_and_servers(void)
+{
+ static time_t lastrun = 0;
+ time_t t = time(NULL);
+
+ if (!lastrun) lastrun = t;
+ if (t < lastrun + 5) return;
+ lastrun = t;
+
+ expire_names(t);
+ expire_servers(t);
+}
+
+/*****************************************************************************
+ 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) {
+ DEBUG(3,("services not loaded\n"));
+ 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;
+
+ pstring ip,name,mask,flags,extra;
+
+ char *ptr;
+ int count = 0;
+ struct in_addr ipaddr;
+ struct in_addr ipmask;
+ enum name_source source = LMHOSTS;
+
+ strcpy(ip,"");
+ strcpy(name,"");
+ strcpy(mask,"");
+ strcpy(flags,"");
+ strcpy(extra,"");
+
+ ptr = line;
+
+ if (next_token(&ptr,ip ,NULL)) ++count;
+ if (next_token(&ptr,name ,NULL)) ++count;
+ if (next_token(&ptr,mask ,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;
+ }
+
+ /* work out if we need to shuffle the tokens along due to the
+ optional subnet mask argument */
+
+ if (strchr(mask, 'G') || strchr(mask, 'S') || strchr(mask, 'M')) {
+ strcpy(flags, mask );
+ /* default action for no subnet mask */
+ strcpy(mask, inet_ntoa(Netmask));
+ }
+
+ DEBUG(4, ("lmhost entry: %s %s %s %s\n", ip, name, mask, flags));
+
+ if (strchr(flags,'G') || strchr(flags,'S'))
+ group = True;
+
+ if (strchr(flags,'M') && !group) {
+ source = SELF;
+ strcpy(myname,name);
+ }
+
+ ipaddr = *interpret_addr2(ip);
+ ipmask = *interpret_addr2(mask);
+
+ if (group) {
+ add_domain_entry(ipaddr, ipmask, name, True);
+ } else {
+ add_netbios_entry(name,0x20,NB_ACTIVE,0,source,ipaddr);
+ }
+ }
+ }
+
+ fclose(f);
+}
+
+
+/****************************************************************************
+ The main select loop.
+ ***************************************************************************/
+static void process(void)
+{
+ BOOL run_election;
+
+ while (True)
+ {
+ time_t t = time(NULL);
+ run_election = check_elections();
+ listen_for_packets(run_election);
+
+ run_packet_queue();
+ run_elections();
+
+ announce_host();
+
+#if 0
+ /* what was this stuff supposed to do? It sent
+ ANN_GetBackupListReq packets which I think should only be
+ sent when trying to find out who to browse with */
+ announce_backup();
+#endif
+
+ announce_master();
+
+ expire_names_and_servers();
+ expire_netbios_response_entries(t-10);
+ refresh_my_names(t);
+
+ write_browse_list();
+ do_browser_lists();
+ check_master_browser();
+ }
+}
+
+
+/****************************************************************************
+ 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,("Sockets 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()
+{
+ 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;
+ }
+
+ 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-G group name add a group name to be part of\n");
+ printf("\t-I ip-address override the IP address\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;
+
+ StartupTime = time(NULL);
+
+ TimeInit();
+
+ strcpy(debugf,NMBLOGFILE);
+
+ setup_logging(argv[0],False);
+
+ charset_initialise();
+
+ ipzero = *interpret_addr2("0.0.0.0");
+
+#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 = ipzero;
+ myip = ipzero;
+
+ 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':
+ if (got_bcast && got_nmask) {
+ add_domain_entry(bcast_ip,Netmask,optarg, True);
+ } else {
+ DEBUG(0, ("Warning: option -G %s added before broadcast and netmask.\n",
+ optarg));
+ DEBUG(0, ("Assuming default values: bcast %s netmask %s\n",
+ inet_ntoa(bcast_ip), inet_ntoa(Netmask))); /* (i hope) */
+ }
+ 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 (!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 %d\n", port));
+
+ if (!open_sockets(is_daemon,port)) 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();
+ add_my_domains();
+
+ DEBUG(3,("Checked names\n"));
+
+ write_browse_list();
+
+ DEBUG(3,("Dumped names\n"));
+
+ 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..7e8cdd67e15
--- /dev/null
+++ b/source/nmbsync.c
@@ -0,0 +1,183 @@
+/*
+ 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 "localnet.h"
+
+
+extern int DEBUGLEVEL;
+
+extern pstring myname;
+extern struct in_addr bcast_ip;
+extern struct in_addr Netmask;
+
+extern int name_type;
+extern int max_protocol;
+extern struct in_addr dest_ip;
+extern int pid;
+extern int gid;
+extern int uid;
+extern int mid;
+extern BOOL got_pass;
+extern BOOL have_ip;
+extern pstring workgroup;
+extern pstring service;
+extern pstring desthost;
+extern BOOL connect_as_ipc;
+
+/****************************************************************************
+fudge for getpass function
+****************************************************************************/
+char *getsmbpass(char *pass)
+{
+ return "dummy"; /* return anything: it should be ignored anyway */
+}
+
+/****************************************************************************
+adds information retrieved from a NetServerEnum call
+****************************************************************************/
+static BOOL add_info(struct domain_record *d, struct work_record *work, int servertype)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ int rdrcnt,rprcnt;
+ char *p;
+ pstring param;
+ int uLevel = 1;
+ int count = -1;
+
+ /* now send a SMBtrans command with api ServerEnum? */
+ 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,uLevel);
+ SSVAL(p,2,0x2000); /* buf length */
+ p += 4;
+ SIVAL(p,0,servertype);
+ p += 4;
+
+ strcpy(p, work->work_group);
+ p = skip_string(p,1);
+
+ if (cli_call_api(PTR_DIFF(p,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)
+ {
+ count=SVAL(rparam,4);
+ p = rdata;
+
+ for (i = 0;i < count;i++, p += 26)
+ {
+ char *sname = p;
+ uint32 stype = IVAL(p,18);
+ int comment_offset = IVAL(p,22) & 0xFFFF;
+ char *cmnt = comment_offset?(rdata+comment_offset-converter):"";
+
+ struct work_record *w = work;
+
+ DEBUG(4, ("\t%-16.16s %08x %s\n", sname, stype, cmnt));
+
+ if (stype & SV_TYPE_DOMAIN_ENUM)
+ {
+ /* creates workgroup on remote subnet */
+ if ((w = find_workgroupstruct(d,sname, False)))
+ {
+ if (ip_equal(bcast_ip, d->bcast_ip))
+ {
+ announce_request(w, d->bcast_ip);
+ }
+ }
+ }
+
+ add_server_entry(d,w,sname,stype,lp_max_ttl(),cmnt,False);
+ }
+ }
+ }
+
+ if (rparam) free(rparam);
+ if (rdata) free(rdata);
+
+ return(True);
+}
+
+
+/*******************************************************************
+ synchronise browse lists with another browse server.
+
+ log in on the remote server's SMB port to their IPC$ service,
+ do a NetServerEnum and update our server and workgroup databases.
+ ******************************************************************/
+void sync_browse_lists(struct work_record *work, char *name, int nm_type,
+ struct in_addr ip)
+{
+ struct domain_record *d;
+ pid = getpid();
+ uid = getuid();
+ gid = getgid();
+ mid = pid + 100;
+ name_type = nm_type;
+
+ got_pass = True;
+
+ DEBUG(4,("sync browse lists with %s for %s %s\n",
+ work->work_group, name, inet_ntoa(ip)));
+
+ strcpy(workgroup,work->work_group);
+ strcpy(desthost,name);
+ dest_ip = ip;
+
+ if (zero_ip(dest_ip)) return;
+ have_ip = True;
+
+ if (!(d = find_domain(ip))) return;
+
+ connect_as_ipc = True;
+
+ /* connect as server and get domains, then servers */
+
+ sprintf(service,"\\\\%s\\IPC$", name);
+ strupper(service);
+
+ if (cli_open_sockets(SMB_PORT))
+ {
+ if (cli_send_login(NULL,NULL,True,True))
+ {
+ add_info(d, work, SV_TYPE_DOMAIN_ENUM);
+ add_info(d, work, SV_TYPE_ALL&~SV_TYPE_DOMAIN_ENUM);
+ }
+
+ close_sockets();
+ }
+}
diff --git a/source/param/loadparm.c b/source/param/loadparm.c
new file mode 100644
index 00000000000..bbeb4801d52
--- /dev/null
+++ b/source/param/loadparm.c
@@ -0,0 +1,1911 @@
+/*
+ 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;
+
+#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;
+ char *szSmbrun;
+ char *szWINSserver;
+ 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 bWINSsupport;
+ BOOL bWINSproxy;
+ 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;
+ BOOL bDeleteReadonly;
+ 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 */
+ False, /* bDeleteReadonly */
+ "" /* 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_STRING, P_GLOBAL, &Globals.szSmbrun, 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},
+ {"wins support", P_BOOL, P_GLOBAL, &Globals.bWINSsupport, NULL},
+ {"wins proxy", P_BOOL, P_GLOBAL, &Globals.bWINSproxy, NULL},
+ {"wins server", P_STRING, P_GLOBAL, &Globals.szWINSserver, 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},
+ {"delete readonly", P_BOOL, P_LOCAL, &sDefault.bDeleteReadonly, 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, "/");
+ string_set(&Globals.szSmbrun, SMBRUN);
+ 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;
+ Globals.bWINSsupport = True;
+ Globals.bWINSproxy = False;
+
+#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:
+ case PRINT_PLP:
+ 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_smbrun,&Globals.szSmbrun)
+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_STRING(lp_wins_server,&Globals.szWINSserver)
+
+FN_GLOBAL_BOOL(lp_wins_support,&Globals.bWINSsupport)
+FN_GLOBAL_BOOL(lp_wins_proxy,&Globals.bWINSproxy)
+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_BOOL(lp_delete_readonly,bDeleteReadonly)
+
+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;
+ else if (strequal(pszParmValue,"plp"))
+ *val = PRINT_PLP;
+ 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..d5d841dceb8
--- /dev/null
+++ b/source/param/params.c
@@ -0,0 +1,327 @@
+/*
+ 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;
+
+
+/**************************************************************************
+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);
+}
+
+
+/**************************************************************************
+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, BOOL (*pfunc)(char *,char *))
+{
+ 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,
+ BOOL (*sfunc)(char *),BOOL (*pfunc)(char *,char *))
+{
+ 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,BOOL (*sfunc)(char *),BOOL (*pfunc)(char *,char *))
+{
+ 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);
+}
+
+
diff --git a/source/params.h b/source/params.h
index 253eaa4e7ae..b3ccdf74d97 100644
--- a/source/params.h
+++ b/source/params.h
@@ -32,14 +32,9 @@ Prototypes and definitions for PARAMS.C.
#include <stdio.h>
#include "smb.h"
-typedef BOOL (* PM_PARMFUNC)(char *pszParmName, char *pszParmValue);
-typedef BOOL (* PM_SECFUNC)(char *pszSectionName);
-
#define PM_NOFILE 1
#define PM_NOFILENAME 2
#define PM_FILEERROR 3
-extern BOOL pm_process(char *pszFileName, PM_SECFUNC sfunc, PM_PARMFUNC pfunc);
-
#endif
diff --git a/source/passdb/smbpass.c b/source/passdb/smbpass.c
new file mode 100644
index 00000000000..35816c5c520
--- /dev/null
+++ b/source/passdb/smbpass.c
@@ -0,0 +1,303 @@
+#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((char *)p,(char *)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((char *)p,(char *)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..2aa27926d9b
--- /dev/null
+++ b/source/printing/printing.c
@@ -0,0 +1,936 @@
+/*
+ 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,jobtime1;
+
+ jobtime = time(NULL); /* default case: take current time */
+ if (count >= minimum) {
+ struct tm *t;
+ int i, day, hour, min, sec;
+ char *c;
+
+ for (i=0; i<13; i++) if (!strncmp(tok[ptr], Months[i],3)) break; /* Find month */
+ if (i<12) {
+ t = localtime(&jobtime);
+ day = atoi(tok[ptr+1]);
+ c=(char *)(tok[ptr+2]);
+ *(c+2)=0;
+ hour = atoi(c);
+ *(c+5)=0;
+ min = atoi(c+3);
+ if(*(c+6) != 0)sec = atoi(c+6);
+ else sec=0;
+
+ if ((t->tm_mon < i)||
+ ((t->tm_mon == i)&&
+ ((t->tm_mday < day)||
+ ((t->tm_mday == day)&&
+ (t->tm_hour*60+t->tm_min < hour*60+min)))))
+ t->tm_year--; /* last year's print job */
+
+ t->tm_mon = i;
+ t->tm_mday = day;
+ t->tm_hour = hour;
+ t->tm_min = min;
+ t->tm_sec = sec;
+ jobtime1 = mktime(t);
+ if (jobtime1 != (time_t)-1)
+ jobtime = jobtime1;
+ }
+ }
+ return jobtime;
+}
+
+
+/****************************************************************************
+parse a lpq line
+
+here is an example of lpq output under bsd
+
+Warning: no daemon present
+Rank Owner Job Files Total Size
+1st tridge 148 README 8096 bytes
+
+here is an example of lpq output under osf/1
+
+Warning: no daemon present
+Rank Pri Owner Job Files Total Size
+1st 0 tridge 148 README 8096 bytes
+****************************************************************************/
+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);
+}
+
+
+/****************************************************************************
+ parse a lpq line for the plp printing system
+ Bertrand Wallrich <Bertrand.Wallrich@loria.fr>
+****************************************************************************/
+static BOOL parse_lpq_plp(char *line,print_queue_struct *buf,BOOL first)
+{
+ string tok[5];
+ int count=0;
+
+ /* handle the case of "(standard input)" as a filename */
+ string_sub(line,"stdin","STDIN");
+ string_sub(line,"(","\"");
+ string_sub(line,")","\"");
+
+ for (count=0; count<8 && next_token(&line,tok[count],NULL); count++) ;
+
+ /* we must get 5 tokens */
+ if (count < 8)
+ return(False);
+
+ /* the 4rd must be integer */
+ if (!isdigit(*tok[3])) return(False);
+
+ /* if the fname contains a space then use STDIN */
+ if (strchr(tok[3],' '))
+ strcpy(tok[3],"STDIN");
+
+ /* only take the last part of the filename */
+ {
+ string tmp;
+ char *p = strrchr(tok[5],'/');
+ if (p)
+ {
+ strcpy(tmp,p+1);
+ strcpy(tok[5],tmp);
+ }
+ }
+
+
+ buf->job = atoi(tok[3]);
+
+ /* calcul de la taille du fichier */
+ if (!isdigit(*tok[7])) { buf->size = atoi(tok[7]) * 1.0 ; }
+ else {
+ string tmp;
+ strcpy(tmp,tok[7]);
+ if (strchr(tok[7],'K')) {
+ strncpy(tok[7],tmp,strlen(tmp)-1);
+ buf->size = atoi(tok[7]);
+ buf->size = buf->size * 1024;
+ }
+ if (strchr(tok[7],'M')) {
+ strncpy(tok[7],tmp,strlen(tmp)-1);
+ buf->size = atoi(tok[7]);
+ buf->size = buf->size * 1024.0 * 1000.0;
+ }
+ if (strchr(tok[7],'G')) {
+ strncpy(tok[7],tmp,strlen(tmp)-1);
+ buf->size = atoi(tok[7]);
+ buf->size = buf->size * 1024.0 * 1000000.0;
+ }
+
+ }
+ buf->status = strequal(tok[0],"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[5],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;
+ case PRINT_PLP:
+ ret = parse_lpq_plp(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/installscripts.sh b/source/script/installscripts.sh
new file mode 100755
index 00000000000..a3defa16e1a
--- /dev/null
+++ b/source/script/installscripts.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+# this script courtesy of James_K._Foote.PARC@xerox.com
+INSTALLPERMS=$1
+BINDIR=$2
+SRCDIR=$3
+
+echo Installing scripts in $BINDIR
+
+for d in $BINDIR; do
+ if [ ! -d $d ]; then
+ mkdir $d
+ if [ ! -d $d ]; then
+ echo Failed to make directory $d
+ exit 1
+ fi
+ fi
+done
+
+cp $SRCDIR/smbtar $BINDIR
+cp $SRCDIR/addtosmbpass $BINDIR
+echo Setting permissions on scripts
+chmod $INSTALLPERMS $BINDIR/smbtar
+chmod $INSTALLPERMS $BINDIR/addtosmbpass
+
+echo Scripts installed
+exit 0
diff --git a/source/script/mkproto.awk b/source/script/mkproto.awk
new file mode 100644
index 00000000000..d7b042652d1
--- /dev/null
+++ b/source/script/mkproto.awk
@@ -0,0 +1,39 @@
+# generate prototypes for Samba C code
+# tridge, June 1996
+
+BEGIN {
+ inheader=0;
+}
+
+{
+ if (inheader) {
+ if (match($0,"[)][ \t]*$")) {
+ inheader = 0;
+ printf "%s;\n",$0;
+ } else {
+ printf "%s\n",$0;
+ }
+ next;
+ }
+}
+
+/^static|^extern/ || !/^[a-zA-Z]/ || /[;]/ {
+ next;
+}
+
+!/^unsigned|^mode_t|^DIR|^user|^int|^char|^uint|^struct|^BOOL|^void|^time/ {
+ next;
+}
+
+
+/[(].*[)][ \t]*$/ {
+ printf "%s;\n",$0;
+ next;
+}
+
+/[(]/ {
+ inheader=1;
+ printf "%s\n",$0;
+ next;
+}
+
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..32f2eb5e7de
--- /dev/null
+++ b/source/smbd/dir.c
@@ -0,0 +1,978 @@
+/*
+ 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)
+{
+ /* OS/2 seems to use -1 to indicate "close all directories" */
+ if (key == -1) {
+ int i;
+ for (i=0;i<NUMDIRPTRS;i++)
+ dptr_close(i);
+ return;
+ }
+
+ if (key < 0 || key >= NUMDIRPTRS) {
+ DEBUG(3,("Invalid key %d given to dptr_close\n",key));
+ return;
+ }
+
+ 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);
+}
+
+/****************************************************************************
+check a filetype for being valid
+****************************************************************************/
+BOOL dir_check_ftype(int cnum,int mode,struct stat *st,int dirtype)
+{
+ if (((mode & ~dirtype) & (aHIDDEN | aSYSTEM | aDIR)) != 0)
+ return False;
+ return True;
+}
+
+/****************************************************************************
+ 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 (!dir_check_ftype(cnum,*mode,&sbuf,dirtype)) {
+ 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..25979d72f5b
--- /dev/null
+++ b/source/smbd/ipc.c
@@ -0,0 +1,2831 @@
+/*
+ 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 -= 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",t); /* 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",t); /* 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,
+ BOOL domains,
+ char *domain, uint32 request)
+{
+ if (*domain == 0)
+ {
+ if (strequal(lp_workgroup(), server->domain)) {
+ return True;
+ }
+ else if (domains)
+ {
+ DEBUG(4,("primary domain:reject %8x %s %s\n",request,server->name,server->domain));
+ return False;
+ }
+ else if ((request & SV_TYPE_DOMAIN_ENUM) &&
+ (server->type & SV_TYPE_DOMAIN_ENUM))
+ {
+ DEBUG(4,("rej:DOM %8x: %s %s\n",server->type,server->name,server->domain));
+ return False;
+ }
+
+ return True;
+ }
+ else
+ {
+ if (strequal(domain, server->domain))
+ {
+ /*
+ if (request == SV_TYPE_LOCAL_LIST_ONLY &&
+ !(server->type & SV_TYPE_LOCAL_LIST_ONLY))
+ {
+ DEBUG(4,("rej:LOC %8x: ok %s %s\n",request,server->name,server->domain));
+ return False;
+ }
+ */
+ if ((request == (SV_TYPE_LOCAL_LIST_ONLY|SV_TYPE_DOMAIN_ENUM)) &&
+ !(server->type&SV_TYPE_DOMAIN_ENUM))
+ {
+ DEBUG(4,("rej:LOCDOM %8x: ok %s %s\n",request,server->name,server->domain));
+ return False;
+ }
+
+ return True;
+ }
+ else if (!domains)
+ {
+ DEBUG(4,("domain:%s %s %s\n",domain,server->name,server->domain));
+ return False;
+ }
+ return True;
+ }
+}
+
+/*******************************************************************
+ 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, BOOL domains, char *domain)
+{
+ 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);
+ }
+
+ /* request for everything is code for request all servers */
+ if (servertype == SV_TYPE_ALL) servertype &= ~SV_TYPE_DOMAIN_ENUM;
+
+ DEBUG(4, ("Servertype search: %8x domains:%s\n", servertype,BOOLSTR(domains)));
+
+ while (!feof(f))
+ {
+ fstring stype;
+ struct srv_info_struct *s;
+ char *ptr = line;
+ BOOL ok = True;
+ *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];
+
+ 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 cope with an old nmbd */
+ strcpy(s->domain,my_workgroup());
+ }
+
+ if (sscanf(stype,"%X",&s->type) != 1) { DEBUG(4,("r:host file ")); ok = False; }
+
+ /* doesn't match up: don't want it */
+ if (!(servertype & s->type)) { DEBUG(4,("r:serv type ")); ok = False; }
+
+ if ((servertype == ~SV_TYPE_DOMAIN_ENUM) &&
+ (s->type & SV_TYPE_DOMAIN_ENUM))
+ {
+ DEBUG(4,("s:all x dom "));
+ ok = False;
+ }
+
+ if (domains && !(domain && *domain && strequal(domain, s->domain)))
+ {
+ if (!(s->type & SV_TYPE_DOMAIN_ENUM))
+ {
+ DEBUG(4,("r:dom enum "));
+ ok = False;
+ }
+ }
+
+ if (ok)
+ {
+ DEBUG(4,("**SV** %20s %8x %25s %15s\n",
+ s->name, s->type, s->comment, s->domain));
+
+ s->type |= SV_TYPE_LOCAL_LIST_ONLY;
+ s->server_added = True;
+ count++;
+ }
+ else
+ {
+ DEBUG(4,("%20s %8x %25s %15s\n",
+ s->name, s->type, s->comment, s->domain));
+ }
+ }
+
+ 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 domains;
+ BOOL domain_request;
+ BOOL local_request = servertype & SV_TYPE_LOCAL_LIST_ONLY;
+
+ /*if (servertype == SV_TYPE_ALL) servertype &= ~(SV_TYPE_DOMAIN_ENUM|SV_TYPE_LOCAL_LIST_ONLY);*/
+ if (servertype == SV_TYPE_ALL) servertype &= ~SV_TYPE_DOMAIN_ENUM;
+
+ domain_request = servertype & SV_TYPE_DOMAIN_ENUM;
+
+ 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 %8x ", str2, servertype));
+ DEBUG(4, ("domains_req:%s ", BOOLSTR(domain_request)));
+ DEBUG(4, ("local_only:%s\n", BOOLSTR(local_request)));
+
+ if (strcmp(str1, "WrLehDO") == 0)
+ {
+ domains = False;
+ }
+ else if (strcmp(str1, "WrLehDz") == 0)
+ {
+ domains = True;
+ StrnCpy(domain, p, sizeof(fstring)-1);
+ }
+
+ if (lp_browse_list())
+ {
+ total = get_server_info(servertype,&servers,domains,domain);
+ }
+
+ data_len = fixed_len = string_len = 0;
+
+ {
+ for (i=0;i<total;i++)
+ {
+ struct srv_info_struct *s = &servers[i];
+ if (filter_server_info(s,domains,domain,local_request|domain_request))
+ {
+ data_len += fill_srv_info(s,uLevel,0,&f_len,0,&s_len,0);
+ DEBUG(4,("fill_srv_info %20s %8x %25s %15s\n",
+ s->name, s->type, s->comment, s->domain));
+
+ 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++)
+ {
+ struct srv_info_struct *s = &servers[i];
+ if (filter_server_info(s,domains,domain,local_request|domain_request))
+ {
+ fill_srv_info(s,uLevel,&p,&f_len,&p2,&s_len,*rdata);
+ DEBUG(4,("fill_srv_info %20s %8x %25s %15s\n",
+ s->name, s->type, s->comment, s->domain));
+ 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);
+
+ 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 (issafe(*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,False,NULL))>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..a43e7f62451
--- /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);
+
+ 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 */
+
+ dot_pos = strchr(fname,'.');
+
+ {
+ 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..31d91912714
--- /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",(char *)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;
+
+ mypasswd = getprpwnam (user);
+ starttime = time (NULL);
+
+ if (result)
+ {
+ mypasswd->ufld.fd_slogin = starttime;
+ 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 = starttime;
+ 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,
+ (char *)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,
+ (char *)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
+ if (is_group)
+ {
+ static char *mydomain = NULL;
+ if (!mydomain)
+ yp_get_default_domain(&mydomain);
+ if (mydomain && innetgr(file_host,remote,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);
+}
+
+
+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 = SMB_PORT;
+ 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));
+
+#if !KEEP_PASSWORD_SERVER_OPEN
+ close(password_client); password_client= -1;
+#endif
+
+ return(True);
+}
+
+
diff --git a/source/smbd/quotas.c b/source/smbd/quotas.c
new file mode 100644
index 00000000000..d5be15264e7
--- /dev/null
+++ b/source/smbd/quotas.c
@@ -0,0 +1,363 @@
+#ifdef QUOTAS
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ support for quotas
+ 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 is one of the most system dependent parts of Samba, and its
+ * done a litle differently. Each system has its own way of doing
+ * things :-(
+ */
+
+#include "includes.h"
+
+
+#ifdef LINUX
+
+#ifdef __KERNEL__
+# undef __KERNEL__
+# include <sys/quota.h>
+# define __KERNEL__
+#else
+# include <sys/quota.h>
+#endif
+
+#include <mntent.h>
+
+/****************************************************************************
+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);
+}
+
+#elif defined(CRAY)
+
+#include <sys/quota.h>
+#include <mntent.h>
+
+/****************************************************************************
+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) ;
+
+}
+
+
+#elif defined(SUNOS5)
+
+#include <devnm.h>
+#include <fcntl.h>
+#include <sys/fs/ufs_quota.h>
+
+/****************************************************************************
+try to get the disk space from disk quotas (solaris 2 version)
+****************************************************************************/
+/* Quota code by Peter Urbanec (amiga@cse.unsw.edu.au) */
+static BOOL disk_quotas(char *path, int *bsize, int *dfree, int *dsize)
+{
+ uid_t user_id, euser_id;
+ int r;
+ struct dqblk D;
+ struct quotctl command;
+ int file;
+ int ret;
+
+ if((file=open(path, O_RDONLY))<0) return(False);
+
+ euser_id = geteuid();
+ user_id = getuid();
+
+ command.op = Q_GETQUOTA;
+ command.uid = euser_id;
+ command.addr = (caddr_t) &D;
+
+ setuid(0); /* Solaris seems to want to give info only to super-user */
+ seteuid(0);
+
+ ret = ioctl(file, Q_QUOTACTL, &command);
+
+ setuid(user_id); /* Restore the original UID status */
+ seteuid(euser_id);
+
+ if (ret < 0) {
+ close(file);
+ DEBUG(2,("disk_quotas ioctl (Solaris) failed\n"));
+ return(False);
+ }
+ close(file);
+
+
+ /* Use softlimit to determine disk space. A user exceeding the quota is told
+ * that there's no space left. Writes might actually work for a bit if the
+ * hardlimit is set higher than softlimit. Effectively the disk becomes
+ * made of rubber latex and begins to expand to accommodate the user :-)
+ */
+
+ *bsize = 512;
+ *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
+ *dsize = D.dqb_bsoftlimit;
+ if(*dfree < 0)
+ {
+ *dfree = 0;
+ *dsize = D.dqb_curblocks;
+ }
+
+DEBUG(5,("disk_quotas for path \"%s\" returning bsize %d, dfree %d, dsize %d\n",
+ path,*bsize,*dfree,*dsize));
+
+ return(True);
+}
+
+#else
+
+#include <sys/quota.h>
+#include <devnm.h>
+
+/****************************************************************************
+try to get the disk space from disk quotas - default version
+****************************************************************************/
+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);
+}
+
+#endif
+
+#else
+/* this keeps fussy compilers happy */
+ void quotas_dummy(void) {}
+#endif /* QUOTAS */
+
diff --git a/source/smbd/reply.c b/source/smbd/reply.c
new file mode 100644
index 00000000000..4472e120aaa
--- /dev/null
+++ b/source/smbd/reply.c
@@ -0,0 +1,3214 @@
+/*
+ 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 NT_WORKAROUND
+ if (passlen2 == 1) {
+ /* apparently NT sometimes sets passlen2 to 1 when it means 0. This
+ tries to work around that problem */
+ passlen2 = 0;
+ }
+#endif
+ 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 (!lp_delete_readonly(SNUM(cnum))) {
+ 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);
+
+ /* try and set the date */
+ set_filetime(Files[fnum].name,mtime);
+
+ close_file(fnum);
+
+ /* 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);
+
+ set_filetime(Files[fnum].name,mtime);
+
+ close_file(fnum);
+
+ 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..206d89423fb
--- /dev/null
+++ b/source/smbd/server.c
@@ -0,0 +1,3770 @@
+/*
+ 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;
+extern pstring debugf;
+extern pstring sesssetup_user;
+
+char *InBuffer = NULL;
+char *OutBuffer = NULL;
+char *last_inbuf = NULL;
+
+BOOL share_mode_pending = False;
+
+/* 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);
+
+/* 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;
+ extern struct current_user current_user;
+
+ if (CAN_WRITE(cnum) && !lp_alternate_permissions(SNUM(cnum))) {
+ if (!((sbuf->st_mode & S_IWOTH) ||
+ Connections[cnum].admin_user ||
+ ((sbuf->st_mode & S_IWUSR) && current_user.uid==sbuf->st_uid) ||
+ ((sbuf->st_mode & S_IWGRP) &&
+ in_group(sbuf->st_gid,current_user.gid,
+ current_user.ngroups,current_user.igroups))))
+ result |= aRONLY;
+ } else {
+ if ((sbuf->st_mode & S_IWUSR) == 0)
+ result |= aRONLY;
+ }
+
+ 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;
+ pstring 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))
+ {
+ char *s;
+ fstring name2;
+ sprintf(name2,"%.6s.XXXXXX",remote_machine);
+ /* sanitise the name */
+ for (s=name2 ; *s ; s++)
+ if (!issafe(*s)) *s = '_';
+ 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);
+}
+
+
+/****************************************************************************
+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);
+
+ 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);
+ }
+
+ if (!Files[fnum].modified) {
+ struct stat st;
+ Files[fnum].modified = True;
+ if (fstat(Files[fnum].fd,&st) == 0) {
+ int dosmode = dos_mode(Files[fnum].cnum,Files[fnum].name,&st);
+ if (MAP_ARCHIVE(Files[fnum].cnum) && !IS_DOS_ARCHIVE(dosmode)) {
+ dos_chmod(Files[fnum].cnum,Files[fnum].name,dosmode | aARCH,&st);
+ }
+ }
+ }
+
+ return(write_data(Files[fnum].fd,data,n));
+}
+
+
+/****************************************************************************
+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);
+}
+
+
+/****************************************************************************
+ 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 the listening socket */
+ close(s);
+
+ /* 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 (%s)\n",
+ pcon->connectpath,strerror(errno)));
+ 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;
+ /* we start at 1 here for an obscure reason I can't now remember,
+ but I think is important :-) */
+ 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();
+ time_t t = time(NULL);
+
+ 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,t);
+ SSVALS(outbuf,smb_vwv15+1,TimeDiff(t)/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
+****************************************************************************/
+static 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,NMB_PORT,SOCK_DGRAM);
+ }
+#endif
+
+ 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) {
+ DEBUG(3,("receive_smb error (%s) exiting\n",strerror(errno)));
+ 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) {
+ extern int password_client;
+ if (!send_keepalive(Client)) {
+ DEBUG(2,("%s Keepalive failed - exiting\n",timestring()));
+ return;
+ }
+ /* also send a keepalive to the password server if its still
+ connected */
+ if (password_client != -1)
+ send_keepalive(password_client);
+ 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
+****************************************************************************/
+static 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 = SMB_PORT;
+ 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);
+
+ init_uid();
+
+ /* 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"));
+
+#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))
+ exit(1);
+
+ /* 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..dcd5379bc1d
--- /dev/null
+++ b/source/smbd/smbrun.c
@@ -0,0 +1,97 @@
+/*
+ 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 3 arguments as uid,gid,command and runs command after
+becoming a non-root user */
+ int main(int argc,char *argv[])
+{
+ int uid,gid;
+
+ close_fds();
+
+ if (argc != 4) exit(2);
+
+ uid = atoi(argv[1]);
+ gid = atoi(argv[2]);
+
+ /* first become root - we may need to do this in order to lose
+ our privilages! */
+#ifdef USE_SETRES
+ setresgid(0,0,0);
+ setresuid(0,0,0);
+#else
+ setuid(0);
+ seteuid(0);
+#endif
+
+#ifdef USE_SETFS
+ setfsuid(uid);
+ setfsgid(gid);
+#endif
+
+#ifdef USE_SETRES
+ setresgid(gid,gid,gid);
+ setresuid(uid,uid,uid);
+#else
+ setgid(gid);
+ setegid(gid);
+ setuid(uid);
+ seteuid(uid);
+#endif
+
+
+ /* paranoia :-) */
+ if (getuid() != uid)
+ return(3);
+
+ if (geteuid() != getuid())
+ return(4);
+
+ /* this is to make sure that the system() call doesn't run forever */
+ alarm(30);
+
+ return(system(argv[3]));
+}
diff --git a/source/smbd/trans2.c b/source/smbd/trans2.c
new file mode 100644
index 00000000000..60e9ae2b294
--- /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 *nameptr;
+ 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 (!dir_check_ftype(cnum,mode,&sbuf,dirtype)) {
+ 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;
+ nameptr = 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);
+ nameptr = 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);
+ nameptr = 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);
+ nameptr = 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);
+ nameptr = 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;
+ /* nameptr = 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(nameptr,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 = SVALS(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 = SVALS(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;
+ int16 dptr_num=SVALS(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/uid.c b/source/smbd/uid.c
new file mode 100644
index 00000000000..625303350a6
--- /dev/null
+++ b/source/smbd/uid.c
@@ -0,0 +1,360 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ uid/user 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 connection_struct Connections[];
+
+static int initial_uid;
+static int initial_gid;
+static int old_umask = 022;
+
+static pstring OriginalDir;
+
+/* what user is current? */
+struct current_user current_user;
+
+/****************************************************************************
+initialise the uid routines
+****************************************************************************/
+void init_uid(void)
+{
+ initial_uid = current_user.uid = geteuid();
+ initial_gid = current_user.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();
+
+ current_user.cnum = -1;
+
+ GetWd(OriginalDir);
+}
+
+
+/****************************************************************************
+ 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)
+#elif defined(USE_SETFS)
+ if (setfsuid(uid) != 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);
+ }
+
+ current_user.uid = uid;
+
+ 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)
+#elif defined(USE_SETFS)
+ if (setfsgid(gid) != 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);
+ }
+
+ current_user.gid = gid;
+
+ 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
+****************************************************************************/
+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"));
+
+ current_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 id = uid;
+
+ if (current_user.cnum == cnum && current_user.id == id) {
+ 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;
+ current_user.groups = Connections[cnum].groups;
+ current_user.igroups = Connections[cnum].igroups;
+ current_user.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;
+ current_user.groups = vuser->user_groups;
+ current_user.igroups = vuser->user_igroups;
+ current_user.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 (current_user.ngroups > 0)
+ if (setgroups(current_user.ngroups,current_user.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);
+
+ current_user.cnum = cnum;
+ current_user.id = id;
+
+ 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 (current_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);
+#elif defined(USE_SETFS)
+ setfsuid(initial_uid);
+ setfsgid(initial_gid);
+#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
+
+ current_user.uid = initial_uid;
+ current_user.gid = initial_gid;
+
+ 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()));
+
+ current_user.cnum = -1;
+
+ return(True);
+}
+
+
+/****************************************************************************
+run a command via system() using smbrun, being careful about uid/gid handling
+****************************************************************************/
+int smbrun(char *cmd,char *outfile)
+{
+ int ret;
+ pstring syscmd;
+ char *path = lp_smbrun();
+
+ if (!file_exist(path,NULL))
+ {
+ DEBUG(0,("SMBRUN ERROR: Can't find %s. Installation problem?\n",path));
+ return(1);
+ }
+
+ sprintf(syscmd,"%s %d %d \"(%s 2>&1) > %s\"",
+ path,current_user.uid,current_user.gid,cmd,
+ outfile?outfile:"/dev/null");
+
+ DEBUG(5,("smbrun - running %s ",syscmd));
+ ret = system(syscmd);
+ DEBUG(5,("gave %d\n",ret));
+ return(ret);
+}
+
+
diff --git a/source/smbd/vt_mode.c b/source/smbd/vt_mode.c
new file mode 100644
index 00000000000..0a4d50c217f
--- /dev/null
+++ b/source/smbd/vt_mode.c
@@ -0,0 +1,490 @@
+/* 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(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
+*/
+int VT_Start_utmp(void)
+{
+ 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
+*/
+int VT_Stop_utmp(void)
+{
+ 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(void)
+{
+ 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(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(int sig)
+{
+ VT_AtExit();
+
+ exit(1);
+}
+
+
+/*
+VT_Start: initialize vt-specific data, alloc pty, spawn shell and send ACK
+*/
+int VT_Start(void)
+{
+ 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(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(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(void)
+{
+ 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/sockspy.c b/source/sockspy.c
deleted file mode 100644
index 806b7379157..00000000000
--- a/source/sockspy.c
+++ /dev/null
@@ -1,302 +0,0 @@
-/*
- USAGE
- sockspy desthost destservice
-
-You install this program in /etc/inetd.conf and /etc/services
-
-For example I have used these entries:
-
-/etc/services:
-spy 8001/tcp spy port
-
-/etc/inetd.conf:
-spy stream tcp nowait tridge /usr/local/smb/sockspy sockspy fjall netbios-ssn
-
-This means any connection to port 8001 will be redirected to
-netbios-ssn on fjall. By playing with these parameters you can easily
-spy on most of the tcp protocols. All packets traversing the link will
-be captured.
-
-NOTE: This program is totally unsupported. I haven't used it for 2
-years, and don't intend to fix the obvious bugs/limitations. I will,
-however, accept contributed patches - or even a total rewrite :-)
-*/
-
-#include <stdio.h>
-#include <strings.h>
-#include <sys/types.h>
-#include <sys/dir.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <netinet/in.h>
-#include <netdb.h>
-
-#include <signal.h>
-
-#include <errno.h>
-#include <sysexits.h>
-
-int trans_num = 0;
-
-#ifndef LOGIN
-#define LOGIN "/tmp/spy.in"
-#endif
-
-#ifndef LOGOUT
-#define LOGOUT "/tmp/spy.out"
-#endif
-
-#ifndef LOGCMD
-#define LOGCMD "/tmp/spy.cmd"
-#endif
-
-FILE *cmd = NULL;
-FILE *login = NULL;
-FILE *logout = NULL;
-
-#define STREQL(a, b) (strcmp(a, b) == 0)
-#define NIL (0)
-
-char DestHost[256]; /* Remote system to connect to */
-char DestObj[256]; /* Remote object/service to connect to */
-
-/* Signal handler for SIGPIPE (write on a disconnected socket) */
-abort()
-{
- if (cmd)
- {
- fprintf(cmd,"writing to disconnected socket!\n");
- fflush(cmd);
- }
- exit(1);
-}
-
-
-main(argc, argv)
-int argc; /* # of command line arguments */
-char *argv[]; /* the command line arguments */
-{
- int client, /* Socket connected to client */
- server; /* Socket to use for server */
-
- trans_num = 0;
-#ifndef NOLOG
- login = fopen(LOGIN,"w");
- logout = fopen(LOGOUT,"w");
- cmd = fopen(LOGCMD,"w");
-#endif
-
- if (cmd)
- {
- fprintf(cmd,"Started server\n");
- fflush(cmd);
- }
-
- /* Check usage */
- if(argc != 3)
- return;
-
- strcpy(DestHost,argv[1]);
- strcpy(DestObj,argv[2]);
-
- /* Time to attempt the connection */
- server = inet_conn(DestHost, DestObj);
-
- if( server < 0 ) {
- exit(EX_CANTCREAT);
- }
-
- /* Just to make the code more readable */
- client = 0;
-
- /* We will abort gracefully when the client or remote system
- goes away */
- signal(SIGPIPE, abort);
-
- /* Now just go and move raw data between client and
- remote system */
- dowork(client, server);
- /* ... NEVER RETURNS ... */
-}
-
-dowork(client, server)
- int client, server;
-{
-
- /* select(2) masks for client and remote */
- int ClientMask, ServerMask;
-
- /* Combined ClientMask and ServerMask */
- int ReadMask;
-
- /* Initialize select(2) masks */
- ClientMask = 1<<client;
- ServerMask = 1<<server;
-
- ReadMask = ClientMask | ServerMask;
-
- /* Now move raw data for the rest of our life between
- client and remote */
- for( ; ; ) {
- /* Local Variables */
- int SelectReadMask;/* select(2) mask modifiable by select(2) */
- int nready; /* status return from select(2) */
-
- do {
- /* Intialize select(2) mask everytime
- as select(2) always modifies it */
- SelectReadMask = ReadMask;
-
- /* Wait for data to be present to be moved */
- errno = 0;
- nready = select(32,&SelectReadMask,(int *)0,(int *)0,NIL);
- } while( nready < 0 && errno == EINTR );
-
- /* select(2) failed, shouldn't happen. Exit abnormally */
- if( nready < 0 )
- exit(EX_SOFTWARE);
-
- /* Favor the client (for no particular reason)
- if s/he is has data */
- if( SelectReadMask & ClientMask )
- {
- if (cmd)
- fprintf(cmd,"client %d\n",nready);
- xfer(client, server,login);
- }
-
- /* Then check on the other guy */
- if( SelectReadMask & ServerMask )
- {
- if (cmd)
- fprintf(cmd,"server %d\n",nready);
- xfer(server, client,logout);
- }
- }
-
- /* NEVER REACHED */
-}
-
-#define BUFSIZE 20000 /* Max bytes to move at a time */
-
-xfer(from, to,file)
- int from, to; /* Move data from "from" to "to" */
- FILE *file;
-{
- static char buf[BUFSIZE]; /* Buffer data to be moved */
- int nready; /* # bytes readable */
- int got; /* # bytes actually being moved */
- int ret;
-
- /* Query the system how many bytes are ready to be read */
- ioctl(from, FIONREAD, &nready);
-
- if (cmd)
- fprintf(cmd,"nready = %d\n",nready);
-
- /* Only try to get the smaller of nready and BUFSIZE */
- got = read(from, buf, nready < BUFSIZE ? nready : BUFSIZE);
-
- /* Zero bytes returned indicates end of stream, exit gracefully */
- if( got == 0 )
- {
- if (cmd)
- {
- fprintf(cmd,"read 0 bytes exiting\n");
- fflush(cmd);
- }
- if (login)
- fclose(login);
- if (logout)
- fclose(logout);
- if (cmd)
- fclose(cmd);
- exit(EX_OK);
- }
-
-
- if (file)
- {
- fprintf(file,"\nTransaction %d\n",trans_num);
- fwrite(buf,got,1,file);
- fflush(file);
- }
- trans_num++;
-
- /* Now send it accross to the other side */
- ret = write(to, buf, got);
-
- if (cmd)
- {
- fprintf(cmd,"wrote %d\n",ret);
- if (ret < 0)
- fprintf(cmd,"error = %s\n",strerror(errno));
- }
-}
-
-int
-inet_conn(host, port)
- char *host;
- char *port;
-{
- /* Local Vars */
- int sock; /* Socket to use for the connection */
- struct hostent *hostent; /* Destination host entry */
- struct servent *servent; /* Destination service entry */
- struct sockaddr_in addr; /* Formated destination for connect */
-
- /* Fetch the requested host and service entries */
- hostent = gethostbyname(host);
- if (isdigit(*port))
- servent = getservbyport(80, "tcp");
- else
- servent = getservbyname(port, "tcp");
-
-
- if (cmd)
- {
- fprintf(cmd,"inet_conn %s %s\n",host,port);
-
- if (servent == NULL)
- fprintf(cmd,"servent is NIL\n");
- if (hostent == NULL)
- fprintf(cmd,"hostent is NIL\n");
- if (hostent->h_addrtype != AF_INET)
- fprintf(cmd,"not inet type\n");
- fflush(cmd);
- }
-
-
- /* No host entry, no service entry, or host is not
- Internet, error! */
- if( servent == NIL ||
- hostent == NIL ||
- hostent->h_addrtype != AF_INET )
- return -1;
-
- /* Get a socket from the system to use for the connection */
- if( (sock = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
- return -1;
-
- /* Make sure we start with a clean address structure ... */
- bzero(&addr, sizeof(addr));
-
- /* ... then fill in the required fields */
- addr.sin_family = AF_INET;
- addr.sin_port = servent->s_port;
- bcopy(hostent->h_addr, &addr.sin_addr, hostent->h_length);
-
- /* Now try to connection to the destination */
- if( connect(sock, &addr, sizeof(addr)) < 0 ) {
- /* No go, release the socket, and then return error! */
- close(sock);
- return -1;
- }
-
- /* Success. Return the connected socket descriptor */
- if (cmd)
- fprintf(cmd,"returning %d\n",sock);
- return sock;
-}
-
-
diff --git a/source/utils/nmblookup.c b/source/utils/nmblookup.c
new file mode 100644
index 00000000000..6289ef74b1e
--- /dev/null
+++ b/source/utils/nmblookup.c
@@ -0,0 +1,219 @@
+/*
+ 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"
+
+extern int DEBUGLEVEL;
+
+extern pstring scope;
+
+extern struct in_addr bcast_ip;
+extern pstring myhostname;
+
+static BOOL got_bcast = False;
+struct in_addr ipzero;
+
+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();
+
+ ipzero = *interpret_addr2("0.0.0.0");
+
+ 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..c79aa15c807
--- /dev/null
+++ b/source/utils/smbpasswd.c
@@ -0,0 +1,457 @@
+#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((char *)p,(char *)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((char *)p, "NO PASSWORD", 11)) {
+ pw_buf.smb_passwd = NULL; /* No password */
+ } else {
+ if(!gethexpwd((char *)p,(char *)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((char *)p,(char *)smbntpwd))
+ pw_buf.smb_nt_passwd = smbntpwd;
+ }
+ }
+ return &pw_buf;
+ }
+ return NULL;
+}
+
+/*
+ * Print command usage on stderr and die.
+ */
+static 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];
+
+ TimeInit();
+
+ 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..65e9d975f14
--- /dev/null
+++ b/source/utils/status.c
@@ -0,0 +1,263 @@
+/*
+ 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;
+extern pstring myhostname;
+
+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;
+
+ TimeInit();
+ 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, "pds:u:")) != 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);
+ }
+
+ get_myname(myhostname, NULL);
+
+ 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);
+ }
+ else if (verbose) {
+ printf("Opened status file %s\n", fname);
+ }
+
+ 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)));
+ }
+ }
+ 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)));
+ }
+ 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..c6fa674b2d3
--- /dev/null
+++ b/source/utils/testparm.c
@@ -0,0 +1,115 @@
+/*
+ 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;
+
+ TimeInit();
+
+ 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..1474f0ecc60
--- /dev/null
+++ b/source/utils/testprns.c
@@ -0,0 +1,74 @@
+/*
+ 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;
+
+ TimeInit();
+
+ 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);
+}
+