summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPawel Salek <pawsa@damage.localdomain>2010-03-04 22:21:56 +0100
committerPawel Salek <pawsa@damage.localdomain>2010-03-04 22:21:56 +0100
commitaf79dfa2d055053c1027e20cf08f0549dc2e9ada (patch)
treec1fbf971ebdf7913e0325313b701e7f2d2fa9c17
downloadlibesmtp-af79dfa2d055053c1027e20cf08f0549dc2e9ada.tar.gz
libesmtp-af79dfa2d055053c1027e20cf08f0549dc2e9ada.tar.xz
libesmtp-af79dfa2d055053c1027e20cf08f0549dc2e9ada.zip
Initial Commit
-rw-r--r--AUTHORS1
-rw-r--r--COPYING340
-rw-r--r--COPYING.LIB515
-rw-r--r--ChangeLog1262
-rw-r--r--INSTALL236
-rw-r--r--Makefile.am30
-rw-r--r--Makefile.in828
-rw-r--r--NEWS229
-rw-r--r--Notes95
-rw-r--r--README106
-rw-r--r--TODO31
-rw-r--r--api.h35
-rw-r--r--auth-client.c471
-rw-r--r--auth-client.h91
-rw-r--r--auth-plugin.h56
-rw-r--r--base64.c167
-rw-r--r--base64.h28
-rw-r--r--concatenate.c193
-rw-r--r--concatenate.h42
-rw-r--r--configure.in618
-rw-r--r--crammd5/Makefile.am14
-rw-r--r--crammd5/Makefile.in479
-rw-r--r--crammd5/client-crammd5.c142
-rw-r--r--crammd5/hmacmd5.c120
-rw-r--r--crammd5/hmacmd5.h51
-rw-r--r--crammd5/md5.c297
-rw-r--r--crammd5/md5.h51
-rw-r--r--doc/api.xml2360
-rw-r--r--errors.c296
-rw-r--r--examples/Makefile15
-rw-r--r--examples/mail-file.c567
-rw-r--r--examples/test-mail15
-rw-r--r--getaddrinfo.c314
-rw-r--r--getaddrinfo.h67
-rw-r--r--gethostbyname.c228
-rw-r--r--gethostbyname.h103
-rw-r--r--headers.c906
-rw-r--r--headers.h32
-rw-r--r--htable.c228
-rw-r--r--htable.h39
-rw-r--r--libesmtp-config.in87
-rw-r--r--libesmtp-private.h256
-rw-r--r--libesmtp.h317
-rw-r--r--libesmtp.spec.in82
-rw-r--r--login/Makefile.am11
-rw-r--r--login/Makefile.in476
-rw-r--r--login/client-login.c135
-rw-r--r--memrchr.c43
-rw-r--r--message-callbacks.c92
-rw-r--r--message-source.c218
-rw-r--r--message-source.h42
-rw-r--r--missing.h57
-rw-r--r--ntlm/Makefile.am11
-rw-r--r--ntlm/Makefile.in478
-rw-r--r--ntlm/client-ntlm.c149
-rw-r--r--ntlm/ntlm.h52
-rw-r--r--ntlm/ntlmdes.c157
-rw-r--r--ntlm/ntlmstruct.c331
-rw-r--r--plain/Makefile.am11
-rw-r--r--plain/Makefile.in476
-rw-r--r--plain/client-plain.c113
-rw-r--r--protocol-states.h51
-rw-r--r--protocol.c1570
-rw-r--r--protocol.h43
-rw-r--r--rfc2822date.c118
-rw-r--r--rfc2822date.h27
-rw-r--r--siobuf.c647
-rw-r--r--siobuf.h59
-rw-r--r--smtp-api.c637
-rw-r--r--smtp-auth.c306
-rw-r--r--smtp-bdat.c314
-rw-r--r--smtp-etrn.c251
-rw-r--r--smtp-tls.c715
-rw-r--r--snprintf.c797
-rw-r--r--strcasecmp.c47
-rw-r--r--strdup.c45
-rw-r--r--strncasecmp.c49
-rw-r--r--tokens.c128
-rw-r--r--tokens.h29
79 files changed, 21125 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..2c0be55
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+Brian Stafford <brian@stafford.uklinux.net>
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d60c31a
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 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
+
+ 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) <year> <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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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) year 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/COPYING.LIB b/COPYING.LIB
new file mode 100644
index 0000000..ba2be48
--- /dev/null
+++ b/COPYING.LIB
@@ -0,0 +1,515 @@
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations
+below.
+
+ When we speak of free software, we are referring to freedom of use,
+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 and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+^L
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it
+becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+^L
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, 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 library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete 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 distribute a copy of this License along with the
+Library.
+
+ 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 Library or any portion
+of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+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 Library, 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 Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+^L
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you 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.
+
+ If distribution of 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 satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+^L
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be 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.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+^L
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library 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.
+
+ 9. 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 Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+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 with
+this License.
+^L
+ 11. 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 Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library 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 Library.
+
+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.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library 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.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser 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 Library
+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 Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+^L
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+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
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "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
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. 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 LIBRARY 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
+LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+^L
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms
+of the ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library.
+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 library's name and a brief idea of what it
+does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper
+mail.
+
+You should also get your employer (if you work as a programmer) or
+your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James
+Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..fbc8c2e
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,1262 @@
+2005-12-16 Stable Version 1.0.4 released
+----------------------------------------
+2005-12-16 Brian Stafford <brian@stafford.uklinux.net>
+ * headers.c
+ Replaced static counter used when generating the default Message-Id
+ header with getpid() to minimise the risk of 2 processes generating
+ the same Message-Id. If the platform provides gettimeofday() this is
+ used to further reduce the possibility of collision.
+ Thanks to Dmitry Maksyoma <dmaks@esphion.com> for spotting this and
+ suggesting the fix.
+
+ * Makefile.am */Makefile.am
+ Replace CFLAGS with AM_CFLAGS to silence warning from automake.
+
+ * Makefile.am COPYING COPYING.LIB
+ Fixed the names of the files with the GPL and LGPL. It seems
+ the LGPL version of COPYING got zapped by autoconf at some time
+ in the past.
+
+2005-08-29 Brian Stafford <brian@stafford.uklinux.net>
+ * acinclude.m4
+ Fix underquoted definition of ACX_WHICH_GETHOSTBYNAME_R. Thanks
+ to Matthias Andree <matthias.andree@gmx.de>.
+
+2005-07-25 Brian Stafford <brian@stafford.uklinux.net>
+ * errors.c
+ Added #ifdefs for some of the EAI_ constants used by getaddrinfo()
+ which are not defined by OSX.
+ Thanks to Thomas Deselaers <deselaers@gmail.com>
+
+2005-07-21 Brian Stafford <brian@stafford.uklinux.net>
+ * acinclude.m4
+ Fix cross compiling issue when detecting snprintf as suggested by
+ Chris Richards <Chris.Richards@red-m.com>
+
+2005-07-02 Brian Stafford <brian@stafford.uklinux.net>
+ * smtp-api.c
+ Plug memory leaks in smtp_destroy_session() and smtp_set_server().
+ Thanks to Bas ten Berge <sam.ten.berge@hccnet.nl> for report and patch.
+ Also reported by Heikki Lindholm <heikki.lindholm@ipnetworks.fi>
+
+2005-02-03 Brian Stafford <brian@stafford.uklinux.net>
+ * protocol.c
+ exts was set with the wrong flag (DSN) when checking if CHUNKING
+ is a required extension.
+
+2004-07-16 Brian Stafford <brian@stafford.uklinux.net>
+ * smtp-tls.c
+ Applied OpenSSL patch from Pawel Salek when checking subjectAltName.
+
+2004-04-20 Stable Version 1.0.3 released
+----------------------------------------
+2004-04-20 Brian Stafford <brian@stafford.uklinux.net>
+ * memrchr.c configure.in
+ Added memrchr() implementation for systems that don't have one.
+
+ * smtp-tls.c
+ Applied patches from Pawel Salek to check subjectAltName for
+ wildcarded domain name when validating server certificate.
+
+
+2004-01-06 Stable Version 1.0.2 released
+--------------------------------------
+2003-12-01 Brian Stafford <brian@stafford.uklinux.net>
+ * smtp-tls.c examples/mail-file.c
+ Applied patch from Pawel Salek.
+
+ * smtp-tls.c
+ Fixed typo in check_file() which prevented it from doing quite
+ the right thing.
+ The domain name check for the server certificate is now implemented
+ using the wildcard match described in RFC 2818.
+ Check_file() and check_directory() return different values for
+ unusable vs absent files.
+
+2003-09-12 Stable Version 1.0.1 released
+--------------------------------------
+2003-09-11 Brian Stafford <brian@stafford.uklinux.net>
+ * protocol.c smtp-auth.c smtp-bdat.c smtp-etrn.c smtp-tls.c
+ More thoroughly check return value from read_smtp_response().
+
+ * libesmtp.h errors.c
+ Added new "Client error" error code. This is just a cop-out,
+ used when an API called by libesmtp fails.
+
+ * base64.c
+ Make conversions immune to NULL source data,
+
+ * examples/mail-file.c
+ Cleaned up some compiler warnings
+
+2003-09-02 Brian Stafford <brian@stafford.uklinux.net>
+ * siobuf.[hc]
+ Added a few extra sio_ calls. Not actually used in libESMTP though.
+
+2003-07-29 Brian Stafford <brian@stafford.uklinux.net>
+ * concatenate.c errors.c getaddrinfo.c headers.c htable.c
+ * protocol.c siobuf.c
+ Don't perform zero length operations using the memxxx() functions.
+ This may avoid segfaults on some platforms or libraries.
+
+ * siobuf.c
+ Improved handling of flushes in sio_write() particularly in the
+ case where data would exactly fill remaining space in the buffer.
+
+2003-07-27 Brian Stafford <brian@stafford.uklinux.net>
+ * rfc2822date.c
+ Correct leap year compensation for January and February in
+ libesmtp_mktime().
+
+2003-07-27 Brian Stafford <brian@stafford.uklinux.net>
+ * examples/Makefile
+ Changed compiler flags from -ansi to -std=c99 and added -W
+
+2003-03-04 Brian Stafford <brian@stafford.uklinux.net>
+ * headers.c
+ Eliminated bug where find_header() could pass -1 to the length
+ argument of memchr() causing a core dump on some architectures.
+
+2003-02-26 Brian Stafford <brian@stafford.uklinux.net>
+ * libesmtp-private.h protocol.c smtp-bdat.c
+ M$ Exchange does not accept a chunk size of 0 in BDAT 0 LAST as
+ explicitly permitted by RFC 3030, *sigh*. Hackish workaround
+ implemented.
+
+2003-01-27 Brian Stafford <brian@stafford.uklinux.net>
+ * configure.in Makefile.am
+ Added DIST_SUBDIRS macro to make sure tarball gets built properly.
+ This one slipped past 'make distcheck' last time for some reason
+ but then autoconf & friends are totally inscruitable.
+
+ * ntlm/ntlmdes.c
+ OpenSSL 0.9.7 changes some typedefs. Changed to suit, should
+ still be compatible with previous OpenSSL versions.
+
+2002-11-09 Stable Version 1.0 released
+--------------------------------------
+2002-11-09 Brian Stafford <brian@stafford.uklinux.net>
+ * configure.in
+ All version 1.0 features enabled by default.
+ --enable-isoc now sets -std=c99 instead of -ansi
+
+ * headers.c
+ Added missing check for NULL pointer in destroy_header_table.
+ Reversed order of freeing header structures and hash table to
+ avoid referencing freed memory. (Wally Yau)
+
+2002-06-24 Brian Stafford <brian@stafford.uklinux.net>
+ * smtp-etrn.c
+ Compilation fails with `./configure --enable-more-warnings=picky
+ --disable-etrn'. Added missing __attribute__ ((unused)) markers
+ to offending function arguments to avoid this.
+
+2002-06-24 Version 1.0rc1 released
+----------------------------------
+2002-06-24 Brian Stafford <brian@stafford.uklinux.net>
+ * configure.in Makefile.am protocol.c protocol-states.h
+ * smtp-api.c smtp-bdat.c libesmtp.h libesmtp-private.h
+ Added experimental support for the SMTP CHUNKING extension.
+
+ * configure.in
+ Enable non-standard AUTH= response by default.
+
+2002-05-31 Brian Stafford <brian@stafford.uklinux.net>
+ * protocol.c smtp-api.c libesmtp.h libesmtp-private.h
+ Added API call to permit protocol timeouts to be set.
+
+ * ntlm/ntlmstruct.c
+ Replaced use of byteswap.h and bswap_{16,32} with locally defined
+ functions.
+
+2002-04-24 Version 0.8.12 released
+----------------------------------
+2002-03-14 Brian Stafford <brian@stafford.uklinux.net>
+ * headers.c
+ Setting Hdr_PROHIBIT did not work properly. Thanks to Ronald
+ F. Guilmette for pointing this out.
+
+2002-03-13 Brian Stafford <brian@stafford.uklinux.net>
+ * protocol.c smtp-api.c libesmtp.h configure.in
+ Revoked deprecated status from smtp_option_require_all_recipients
+ and remove the corresponding --enable-require-all-recipients
+ parameter to configure.
+
+2002-03-07 Brian Stafford <brian@stafford.uklinux.net>
+ * libesmtp.h libesmtp-private.h protocol.c smtp-tls.c
+ RFC 2487 is obsoleted by RFC 3207. Updated references.
+
+ * protocol.c
+ The check for required STARTTLS was omitted when processing the
+ HELO command. If a server did not implement EHLO the session
+ would proceed instead of quitting. Check added and the event
+ callback added to report the missing extension to the
+ application.
+
+2002-03-06 Version 0.8.11 released
+----------------------------------
+
+2002-03-04 Brian Stafford <brian@stafford.uklinux.net>
+ * protocol.c
+ Fix buffer overflow problem in read_smtp_response. This
+ overflow could be exploited by a malicious SMTP server to
+ overwrite the stack and hence a carefully crafted response could
+ cause arbitrary code to be executed. Also took the opportunity
+ to add a related check for a potential DoS attack which makes
+ use of excessively long SMTP responses. Thanks to Colin Phipps
+ for detecting this.
+
+ * concatenate.[ch]
+ New function cat_shrink to shrink-wrap the allocated buffer.
+
+ * libesmtp.h errors.c
+ New unterminated response error code and description.
+
+ * ntlm/ntlmstruct.c configure.in crammd5/md5.h
+ stdint.h does not yet seem to be widely available causing
+ compilation to fail on some platforms. Changed uint{16,32}_t to
+ unsigned{16,32}_t, detect correct sizes with autoconf and added
+ typedefs in ntlmstruct.c. Changed detection types from int to
+ unsigned int in configure.in and made corresponding changes in
+ crammd5/md5.h. Thanks to Ronald F. Guilmette for spotting this.
+
+
+2002-02-12 Brian Stafford <brian@stafford.uklinux.net>
+ * strcasecmp.c strncasecmp.c
+ These now return the correct sign of result for differing strings.
+
+2002-01-30 Version 0.8.10p1 released
+------------------------------------
+
+2002-01-29 Brian Stafford <brian@stafford.uklinux.net>
+ * ntlm/Makefile.am
+ Added ntlm.h to list of sources. This omission stopped 0.8.10
+ form building.
+
+2002-01-29 Version 0.8.10 released
+----------------------------------
+
+2002-01-26 Brian Stafford <brian@stafford.uklinux.net>
+ * various files
+ Copyright messages now show the correct year.
+ Minor tweaks to kill warnings when compiling with
+ --enable-more-warnings=picky. In a few cases this meant adding
+ a few casts which superficially look unnecessary. In other
+ cases this meant adding a number of #undefs to get a vanilla
+ ISOC environment.
+
+ * missing.h
+ Added missing.h which has declarations for Posix/SUS functions
+ which may be missing from system libraries on some platforms.
+
+ * snprintf.c configure.in
+ Detect broken or missing snprintf() implementations and replace
+ if necessary. N.B. the replacement snprintf.c is taken from the
+ libmutt distro and it too, is broken. However, it *does*
+ correctly truncate and \0 terminate output which is too long to
+ fit in the buffer and that is the behaviour I rely on.
+
+ * strdup.c
+ Added strdup() for systems which don't have it.
+
+ * examples/mail-file.c
+ Check for errors when smtp_start_session returns. Fixed
+ authinteract so that responses are not accidentally overwritten.
+
+2002-01-24 Brian Stafford <brian@stafford.uklinux.net>
+ * htable.c configure.in strndup.c
+ Altered code to avoid the use of strndup. strndup.c is removed
+ from the distribution.
+
+2002-01-16 Brian Stafford <brian@stafford.uklinux.net>
+ * ntlm/* configure.in
+ Added NTLM auhentication module.
+
+2002-01-07 Brian Stafford <brian@stafford.uklinux.net>
+ * concatenate.c errors.c siobuf.c
+ Check return value from snprintf.
+
+2002-01-03 Version 0.8.9 released
+---------------------------------
+
+2002-01-02 Brian Stafford <brian@stafford.uklinux.net>
+ * configure.in
+ Added -lsocket to list of libraries searched for getaddrinfo().
+
+2001-12-29 Brian Stafford <brian@stafford.uklinux.net>
+ * protocol.c configure.in
+ Added hack for stupid SMTP servers that advertise AUTH using
+ non-standard syntax from an internet draft that never made it
+ into RFC 2554. Because this feature is non-standard, it must
+ be explicitly enabled when configuring.
+ rsp_{helo,ehlo}() now reset the auth mechanism list before
+ processing the result. Previously this was done only when AUTH
+ was advertised.
+
+ * smtp-auth.c
+ set_auth_mechanisms no longer resets the mechanism list
+ before processing. Added a test to avoid duplicates in the
+ mechanism list. select_auth_mechanism now guarantees to select
+ the *first* usable mechanism. The net effect of these changes
+ is that multiple calls to set_auth_mechanisms accumulate.
+
+ * auth-client.c
+ Rearranged code in auth_set_mechanisms and load_client_plugin
+ avoiding the need to repeat the test for plugin acceptability.
+
+2001-12-24 Brian Stafford <brian@stafford.uklinux.net>
+ * configure.in
+ Compiling with picky warnings turned on was broken. Also,
+ recent glibc versions seem to have decided that strcasecmp and
+ a few other functions are GNU extensions causing compiles to
+ fail because of missing declarations. Naturally, autoconf does
+ not detect this. Added a _GNU_SOURCE define to fix this on
+ potentially affected systems. No, I don't like it either.
+
+ * strcasecmp.c strncasecmp.c strndup.c
+ Added these functions in case some systems don't provide them.
+
+2001-12-21 Brian Stafford <brian@stafford.uklinux.net>
+ * htable.[ch]
+ h_insert now returns a void pointer to the data instead of a
+ struct h_node eliminating the need for the h_dptr macro and
+ for code using hash tables to maintain two pointers instead
+ of one.
+
+ * headers.c
+ Updated to use the simpler hash table interface.
+
+2001-12-10 Brian Stafford <brian@stafford.uklinux.net>
+ * auth-client.c
+ Use dlsym and friends directly on platforms that have it.
+
+ * configure.in Makefile.am
+ Detect dlsym, fall back to using libltdl for other platforms.
+ libltdl is no longer distributed significantly reducing tarball
+ size.
+
+2001-12-10 Brian Stafford <brian@stafford.uklinux.net>
+ * configure.in
+ A missing comma caused the test for getipnodebyname to fail on
+ systems which provide it.
+
+2001-12-06 Version 0.8.8 released
+---------------------------------
+
+2001-11-30 Brian Stafford <brian@stafford.uklinux.net>
+ * crammd5/md5.h
+ The len parameter of md5_update differed in type between
+ prototype and definition, preventing compilation if size_t
+ is not an unsigned int.
+
+2001-11-29 Brian Stafford <brian@stafford.uklinux.net>
+ * crammd5/*.[ch]
+ Moved include of config.h from hmacmd5.h to hmacmd5.c. Make
+ sure sys/types.h is included since size_t is used.
+
+ * configure.in
+ Added some extra nonsense for systems which redefine
+ getaddrinfo to something else in netdb.h
+
+2001-11-27 Brian Stafford <brian@stafford.uklinux.net>
+ * configure.in errors.c
+ Add test for broken strerror_r on OSF-1.
+
+2001-11-12 Brian Stafford <brian@stafford.uklinux.net>
+ * configure.in
+ Updated the tests for pthreads. Should now supply the correct
+ compiler flags on more systems.
+
+2001-11-07 Version 0.8.7 released
+---------------------------------
+
+2001-11-05 Brian Stafford <brian@stafford.uklinux.net>
+ * errors.h libesmtp.h
+ Improve handling of error codes from getaddrinfo. Delay mapping
+ of codes to make debugging easier. libesmtp.h defines new error
+ codes for the relevant EAI_XXX codes from getaddrinfo.
+ smtp_strerror will use gai_strerror if appropriate.
+
+2001-10-31 Brian Stafford <brian@stafford.uklinux.net>
+ * configure.in
+ Added test for sun platforms and define __EXTENSIONS__ so that
+ sun's netdb.h will declare the getaddrinfo stuff. (James McPherson)
+
+ * crammd5/md5.[ch]
+ Type sanity: change u_intXX_t to uintXX_t. Also changed the
+ argument for the buffer and length to void * and size_t
+ respectively in md5_update. Buffer for md5_final is now
+ unsigned char.
+
+2001-10-17 Brian Stafford <brian@stafford.uklinux.net>
+ * headers.c
+ Fixed a core dump bug which strikes when existing headers in a
+ message are substituted.
+
+ * Makefile.am
+ Reinstated libesmtp.spec into tarballs.
+
+2001-08-17 Version 0.8.6 released
+---------------------------------
+
+2001-10-17 Brian Stafford <brian@stafford.uklinux.net>
+ * libesmtp-config.in
+ Corrected output for --cflags. Added --numeric-version to make it
+ simpler for configure scripts to compare version numbers.
+
+ * configure.in libesmtp-spec.in
+ Merged changes from Cristophe Lambin. Spec file now creates
+ libesmtp and libesmtp-devel packages. If OpenSSL is used
+ spec file will have openssl dependencies added.
+
+ * Makefile.am
+ Make sure libesmtp.spec and config.h do not make their way into
+ tarballs. These confused the build on some platforms.
+
+2001-10-16 Brian Stafford <brian@stafford.uklinux.net>
+ * configure.in
+ Added --with-openssl[=DIR] option, removed --enable-starttls.
+ OpenSSL dependent features are now enabled or disabled en masse
+ using --with-openssl.
+
+ * crammd5/md5.[ch] crammd5/Makefile.am
+ Added public domain MD5 implementation to crammd5 module. This
+ enables the CRAM-MD5 mechanism to be built, even if OpenSSL is
+ not available.
+
+ * smtp-tls.c
+ Applied patch from James McPherson correcting __attribute to
+ __attribute__
+
+2001-08-05 Version 0.8.5 released
+---------------------------------
+
+2001-10-05 Brian Stafford <brian@stafford.uklinux.net>
+ * libesmtp.spec.in
+ Make sure libesmtp-config gets installed!
+
+ * configure.in
+ Removed STARTTLS's experimental status. The code works and just
+ needs debugging. Certificate management is basic but usable.
+ Set defines for strict iso/posix/xopen in headers only when
+ --enable-isoc is in force. This helps avoid disabling the
+ tm_gmtoff member in the BSD struct tm unnecessarily.
+
+ * siobuf.[ch]
+ sio_read/write use void buffers rather than char.
+
+2001-09-28 Brian Stafford <brian@stafford.uklinux.net>
+ * smtp-tls.c
+ Use the event callback to report STARTTLS in use if the security
+ level was OK.
+
+ * rfc2822date.c
+ Provide a function to portably calculate the timezone offset
+ when struct tm does not provide tm_gmtoff.
+
+ * configure.in
+ Don't bother to check for gmtime[_r] since it isn't used any more.
+
+2001-09-26 Brian Stafford <brian@stafford.uklinux.net>
+ * headers.c
+ Make sure set_to accepts NULL for the mailbox value. Added set_cc
+ which is same as set_to except it fails with a NULL mailbox.
+
+ * Most files.
+ Changed references to RFC 821/822 to RFC 2821/2822 respectively.
+
+2001-09-24 Brian Stafford <brian@stafford.uklinux.net>
+ * headers.c
+ RFC 2822 requires only the originator and date headers to
+ be present in a message. In particular, the presence of the To:
+ header is no longer required. RFC 2822's restriction that
+ headers may not appear multiple times in a messge is enforced
+ respecting certain special exceptions.
+
+ * examples/mail-file.c
+ Added API call to make sure a To: header is generated if not
+ in the message.
+
+2001-09-14 Brian Stafford <brian@stafford.uklinux.net>
+ * configure.in acinclude.m4 acconfig.h
+ Reverted to configure.in, reinstated acconfig.h and added
+ some compatibility stuff to acinclude.m4. All this to try
+ and be compatible with autoconf 2.5 *and* 2.13. I really
+ hate autoconf.
+
+2001-09-05 Brian Stafford <brian@stafford.uklinux.net>
+ * getaddrinfo.c
+ Check if NO_ADDRESS is defined and different to NO_DATA
+
+2001-08-27 Brian Stafford <brian@stafford.uklinux.net>
+ * protocol.c smtp-tls.c
+ Move some detail of selecting STARTTLS to smtp-tls.c
+
+ * smtp-tls.c
+ Changed STARTTLS policy for Starttls_ENABLED. If a server offers
+ STARTTLS, then it must be used and all security requirements must
+ be met. If STARTTLS is not offered the session continues in
+ cleartext. Previously, Starttls_ENABLED permitted a session to
+ continue with possibly compromised security.
+
+2001-08-22 Brian Stafford <brian@stafford.uklinux.net>
+ * smtp-tls.c libesmtp.h
+ More certificate management. Added TLS event reporting.
+
+ * smtp-auth.c
+ Fixed behaviour for zero length responses to server challenges.
+
+ * base64.c
+ Zero length passwords caused an assertion failure in
+ base64_encode. base64_decode did not correctly strip blanks
+ from strings not terminated by \0. Neither did it correctly
+ handle zero length strings.
+
+2001-08-21 Brian Stafford <brian@stafford.uklinux.net>
+ * smtp-tls.c libesmtp.h
+ Added preliminary code for client certificate management.
+
+ * siobuf.[ch]
+ Changed sio_set_tlsclient_ctx to sio_set_tlsclient_ssl. This
+ makes things slightly more flexible for supplying different
+ client certificates according to the remote host.
+
+2001-08-20 Brian Stafford <brian@stafford.uklinux.net>
+ * message-source.c
+ Fixed memory leak in msg_source_destroy. (Pawel Salek)
+
+2001-08-16 Brian Stafford <brian@stafford.uklinux.net>
+ * configure.ac Makefile.am
+ Change from using LIBOBJS to LTLIBOBJS. This prevents the
+ wrong objects from being linked by libtool when building
+ dynamic libraries. Added code to configure.ac to correctly
+ set LTLIBOBJS and LTALLOCA.
+
+ * smtp-api.c protocol.c errors.c libesmtp-private.h configure.ac
+ Code now exclusively uses getaddrinfo. Removed #ifdef code for
+ gethostbyname. Added conditionals for using alternative lwres
+ library distributed with recent versions of bind. Delete
+ --enable-gethostbyname option. Add --enable-emulate-getaddrinfo.
+
+2001-08-14 Brian Stafford <brian@stafford.uklinux.net>
+ * siobuf.c
+ Remove unnecessary socket include files.
+
+ * getaddrinfo.[ch]
+ Added emulation of the RFC 2553 getaddrinfo resolver interface
+ for systems that don't have it.
+
+2001-08-13 Version 0.8.4 released
+---------------------------------
+
+2001-08-13 Brian Stafford <brian@stafford.uklinux.net>
+ * protocol.c
+ Completely ignore TLS extension if TLS is already in use.
+
+ * smtp-tls.c
+ Fix wrong comparison when initialising OpenSSL mutexes. Record
+ the fact that TLS is in use. Change a numeric constant to its
+ symbolic equivalent.
+
+ * crammd5/client-crammd5.c
+ Correct a typo which prevented the hmac computation being
+ correctly rendered in hexadecimal.
+
+ * examples/mail-file.c
+ Added --tls and --require-tls options and supporting code.
+
+2001-07-31 Brian Stafford <brian@stafford.uklinux.net>
+ * configure.ac
+ Make plugin directory consistent with RPM.
+
+ * libesmtp.spec
+ Applied patch from Pawel Salek to run ldconfig after installing.
+
+2001-07-30 Brian Stafford <brian@stafford.uklinux.net>
+ * protocol.c configure.ac
+ Check for uname and use it in preference to gethostname which
+ is not Posix.
+
+2001-07-19 Brian Stafford <brian@stafford.uklinux.net>
+ * configure.ac
+ Check for the presence of the OpenSSL headers as well as
+ the libraries. Remove --enable-callbacks option.
+
+ * smtp-api.c libesmtp.h
+ Added smtp_version API call.
+
+ * message-callbacks.c
+ Removed callbacks which did \n -> CRLF translation.
+
+ * examples/mail-file.c
+ Use libESMTP provided callback unless the --crlf option is
+ supplied.
+
+2001-07-07 Brian Stafford <brian@stafford.uklinux.net>
+ * protocol.c smtp-api.c
+ Only include netinet/in.h if it is actually needed.
+
+2001-07-06 Version 0.8.3 released
+---------------------------------
+
+2001-07-06 Brian Stafford <brian@stafford.uklinux.net>
+ * examples/mail-file.c
+ Made --help more helpful. Undocumented --no-crlf now renamed
+ to --crlf and documented. When prompting for authentication
+ now reads /dev/tty instead of stdin.
+
+ * configure.ac
+ Check for -lsocket.
+
+ * protocol.c siobuf.c
+ Zero errno before calling certain functions. Normally the value
+ of errno is only tested if the preceeding system call or function
+ wrapping the system call failed. However, in a few cases, the
+ functions are called in a loop and the value of errno might
+ be tested after a successful return. This meant that a test
+ on the value of errno might yield an invalid result, sometimes
+ causing the connection to the server to be incorrectly dropped.
+ Unfortunately this effect depended on the amount of data buffering
+ provided by the server!
+
+2001-06-29 Brian Stafford <brian@stafford.uklinux.net>
+ * protocol.c et al.
+ Added support for sendmail specific XUSR extension. This informs
+ sendmail the message is a user submission instead of relay,
+ so it makes sense to issue the command. Whether it actually
+ does anything ...
+
+ * siobuf.c
+ Fixed return from poll in raw_read and raw_write so that EINTR
+ is correctly handled.
+
+2001-06-26 Brian Stafford <brian@stafford.uklinux.net>
+ * auth-client.c
+ Fixed a signed/unsigned comparison that stops compilation
+ when using -Werror.
+
+2001-06-26 Version 0.8.2 released
+---------------------------------
+
+2001-06-26 Brian Stafford <brian@stafford.uklinux.net>
+ * protocol.c siobuf.c
+ Resolved a problem related to blocking/non-blocking polling
+ for server events. This could lead to deadlock with certain
+ servers.
+
+2001-06-24 Brian Stafford <brian@stafford.uklinux.net>
+ * configure.ac most C sources
+ Added --disable-isoc option. When using gcc, -ansi -pedantic
+ are now specified by default since the code compiles without
+ warnings when using both flags.
+ Added --enable-debug option to control DEBUG and NDEBUG
+ definitions. Assert macros used to check arguments to
+ most internal functions.
+
+2001-06-23 Brian Stafford <brian@stafford.uklinux.net>
+ * message-source.c
+ msg_gets now checks for both \r and \n when searching for line
+ endings.
+
+ * errors.c
+ API function now includes ommitted the arguments check.
+
+2001-06-22 Brian Stafford <brian@stafford.uklinux.net>
+ * protocol.c smtp-api.c configure.ac
+ Now uses RFC 2553 / Posix protocol independent getaddrinfo where
+ possible instead of gethostbyname family of resolver functions.
+
+ * gethostbyname.c configure.ac
+ Added ability to use getipnodebyname and corresponding test
+ for configure.
+
+2001-06-18 Brian Stafford <brian@stafford.uklinux.net>
+ * auth-client.c
+ More thorough argument checking on the auth_xxx APIs.
+ Added some missing malloc return value checking.
+ When loading a plugin, make sure it provides a response()
+ function.
+
+ * smtp-auth.c
+ Added some missing malloc return value checking.
+
+ * various sources
+ Changed some 'int' types to 'size_t'
+
+ * message-source.c
+ In msg_gets, an inconsistent pointer could cause a segfault
+ after a realloc which moved the original memory block.
+ Increased sizes of malloc/realloc so that RFC 2821 maximium
+ line length will not cause realloc.
+ Added missing malloc/realloc return value checking.
+
+ * protocol.c
+ If an error occurs while copying the message to the SMTP
+ server drop the connection without terminating the message.
+
+2001-06-15 Version 0.8.1 released
+---------------------------------
+
+2001-06-13 Brian Stafford <brian@stafford.uklinux.net>
+ * configure.in
+ is now configure.ac to suit autoconf 2.5
+ Eliminated some redundant stuff concerned with libtool.
+ Now uses AC_HELP_STRING macro where appropriate.
+ Now use standard AC_FUNC_STRERROR_R macro.
+ Improved checking for time.h and sys/time.h.
+
+ * libesmtp-config.in
+ If libltdl was installed, the list of libraries was set to the
+ wrong thing.
+
+ * errors.c
+ Only use strerror_r if it actually works.
+
+ * rfc822date.c
+ Try sys/time for struct tm just in case!
+
+2001-06-12 Brian Stafford <brian@stafford.uklinux.net>
+ * configure.in
+ Now checks -lnsl when seraching for gethostbyname_r.
+ Only print a warning if strerror_r is not found.
+ Chose much more picky compiler warnings when using gcc - this
+ has knock on effects through many files. Compiles should now
+ be much cleaner on more platforms.
+
+ * siobuf.h
+ Gcc will now check sio_printf()'s argument types against the
+ format string.
+
+ * headers.c
+ Eliminated a variable which was set but not used.
+ The as yet unimplemented smtp_set_resent_headers API will
+ succeed if `onoff' is zero.
+
+ * protocol.c
+ Eliminated variables which were set but not used.
+ Fixed an uninitialised variable bug which might strike if the
+ EHLO command received a 5xx status code. This is likely with
+ older servers and may result in libESMTP dropping the connection
+ instead of trying HELO.
+ Fixed an uninitialised variable bug which could cause the protocol
+ to QUIT inadvertently after processing the response to EHLO.
+
+ * errors.c
+ Rewrote handling of the thread specific data.
+
+2001-06-12 Version 0.8.0 released
+---------------------------------
+
+2001-06-11 Brian Stafford <brian@stafford.uklinux.net>
+ * protocol.c smtp-api.c
+ DELIVERBY extension done - still to test.
+
+2001-06-09 Brian Stafford <brian@stafford.uklinux.net>
+ * message-callbacks.c libesmtp.h
+ Added standard callback functions for reading messages. libesmtp.h
+ provides macros to simplify using them.
+
+2001-06-07 Brian Stafford <brian@stafford.uklinux.net>
+ * message-source.c
+ Changed the declaration of the message callback to make it clearer
+ that the first argument in fact points to internal state allocated
+ by the callback. Strings returned to code reading the message
+ are now const char *.
+
+2001-05-31 Brian Stafford <brian@stafford.uklinux.net>
+ * smtp-api.c protocol.c configure,in
+ Had another go at the smtp_require_all_recipients() API hack.
+ The original implementation hoped the SMTP server would report
+ failure on receiving a zero length message but this isn't
+ reliable. This API must be explicitly enabled by ./configure.
+
+2001-05-28 Brian Stafford <brian@stafford.uklinux.net>
+ * smtp-auth.c
+ Make sure the client won't attempt to authenticate when already
+ authenticated. This could happen if having authenticated and
+ enables a security layer, the server offers AUTH again.
+
+ * smtp-tls.c
+ Make sure the client won't attempt to negotiate TLS when already
+ using TLS. Also don't use TLS if already authenticated.
+
+ * siobuf.c
+ Make the code for non-blocking sockets + OpenSSL more robust.
+
+ * errors.c
+ Added default case for set_herrno().
+
+2001-05-25 Brian Stafford <brian@stafford.uklinux.net>
+ * smtp-auth.c
+ On authentication failure the same mechanism was selected again
+ instead of moving on to the next one. This caused an infinite
+ loop of failing AUTH exchanges.
+
+2001-05-24 Brian Stafford <brian@stafford.uklinux.net>
+ * libesmtp.spec.in
+ Changed "-a 0" option in %setup macro to "-T -b 0"
+
+ * configure.in
+ Removed -Werror from --enable-more-warnings=yes as this can be
+ bothersome for punters. Added --enable-more-warnings=picky to
+ stop gcc from using internal prototypes for builtin functions;
+ also turns on -Werror.
+
+ * protocol.c
+ free_ghbnctx() was called twice if connect() failed, potentially
+ causing a SIGSEGV. This bug was introduced with support for
+ gethostbyname_r.
+
+2001-05-23 Brian Stafford <brian@stafford.uklinux.net>
+ * configure.in
+ Incremented library version and reset the age. This is important
+ because the event callback semantics have changed.
+ Detect IPv6 sockaddr structure in <netinet/in.h>.
+
+ * protocol.c
+ Added 8BITMIME support. New API call smtp_8bitmime_set_body().
+ Report extensions after final set is known. STARTTLS or AUTH
+ can change the set of extensions advertised by the server.
+ Typo meant the RET=FULL/HDRS parameter was printed as
+ SIZE=FULL/HDRS in MAIL FROM: (D'oh!)
+
+ * errors.c
+ Changed prototype for smtp_strerror() to allow use of strerror_r.
+
+2001-05-22 Brian Stafford <brian@stafford.uklinux.net>
+ * siobuf.c
+ Fixed calls to encode/decode callbacks and added explanation of
+ their semantics. This eliminates potential for a buffer
+ overflow bug when decoding expands data read from the socket.
+
+ * libesmtp.spec.in
+ Fixed inconsistency between package name and tarball.
+ Use the bz2 version of the tarball as the source.
+
+ * Makefile.am
+ Added libesmtp.spec to extra distribution files.
+
+ * gethostbyname.c
+ Added missing #include <string.h> (gcc builtin prototypes
+ again - grumble....)
+
+2001-05-21 Brian Stafford <brian@stafford.uklinux.net>
+ * siobuf.c
+ Restructuring of reading/writing and polling to permit use
+ of non-blocking IO.
+
+ * protocol.c
+ Revised protocol outer loop makes sure the protocol engine reads
+ data as soon as it becomes available and defers buffer flushes
+ until after pending data from the SMTP server has been read. In
+ conjunction with non-blocking output this avoids a potential
+ deadlock described in RFC 2920 when PIPELINING is in use.
+
+2001-05-20 Brian Stafford <brian@stafford.uklinux.net>
+ * smtp-etrn.c
+ Added experimental support for the ETRN extension.
+
+ * protocol.c smtp-api.c
+ Check for failure to create a message source and added code
+ to actually destroy it thus plugging a memory leak.
+ More thorough checking of some API function arguments.
+
+ * siobuf.c
+ Added sio_mark(). When the write buffer is flushed data written
+ beyond the mark is retained and the mark is deleted.
+
+ * protocol.c
+ Added new event types for flagging required extensions not
+ available or reporting extensions that provide information to
+ the application.
+ Command boundaries are marked in the write buffer. This
+ prevents partial commands being sent to the SMTP server.
+
+2001-05-18 Version 0.7.1 released
+---------------------------------
+
+2001-05-18 Brian Stafford <brian@stafford.uklinux.net>
+ * protocol.c
+ Added AF_INET6 support.
+
+2001-05-17 Brian Stafford <brian@stafford.uklinux.net>
+ * protocol.c gethostbyname.[ch] configure.in acinclude.m4
+ gethostbyname_r() now in its own file which provides a consistent
+ interface. configure selects which version of the function to
+ compile for when building threaded code.
+
+ * configure.in auth-client.h libesmtp-config.in libesmtp.spec.in
+ Directory for installing authentication plugins is now
+ configurable.
+
+2001-05-15 Brian Stafford <brian@stafford.uklinux.net>
+ * libesmtp.spec.in
+ Added to simplify building RPM packages.
+
+2001-05-14 Brian Stafford <brian@stafford.uklinux.net>
+ * smtp-api.c
+ Check that all messages have a callback to read the message
+ headers and body.
+
+ * tokens.c
+ Check buffer length in read_atom().
+ Use of <ctype.h> eliminated.
+
+ * headers.c
+ init_header_table() checks for NULL pointers to avoid potential
+ SIGSEGVs.
+
+2001-05-13 Brian Stafford <brian@stafford.uklinux.net>
+ * rfc822date.c configure.in
+ Use localtime_r() or gmtime_r() when building a thread safe
+ library.
+
+ * concatenate.[ch]
+ Fixed incorrect shortfall caclulation in concatenate()
+ potentially leading to buffer overrun.
+ Generally tidied up code.
+
+ * auth-client.c
+ auth_response() fails if (*context->client->init)() fails.
+
+ * base64.c
+ b64_encode() now checks the destination buffer length.
+
+2001-05-11 Brian Stafford <brian@stafford.uklinux.net>
+ * libesmtp-config.in Makefile.am
+ Added config script to simplify compiling and linking.
+
+ * protocol.c configure.in
+ Use gethostbyname_r() when building a thread safe library.
+
+2001-05-09 Brian Stafford <brian@stafford.uklinux.net>
+ * Makefile.am configure.in
+ libltdl is now part of the tarball and is installed if not
+ already present.
+
+ * protocol.c
+ do_session() will now make use of all the addresses returned
+ by gethostbyname(). This allows the DNS admin for the domain
+ to specify a number of MTAs which handle mail submission.
+ Failures trying to connect or when processing the greeting or
+ a response to the EHLO/HELO commands will cause a fallback server
+ to be tried. The name server will round robin the responses
+ balancing the load among the servers.
+ When reading the server greeting accept only 220 otherwise the
+ connection may have been made to a non-SMTP service.
+
+2001-05-06 Version 0.7.0 released
+---------------------------------
+
+2001-05-06 Brian Stafford <brian@stafford.uklinux.net>
+ * protocol.c
+ session->auth_mechanisms was incorrectly freed in do_session().
+ This should have been done using destroy_auth_mechanisms().
+ Moved initialisation of the session variables to the start
+ of do_session(). This allows checks for some error conditions
+ to be done before attempting to connect to the SMTP server.
+ Updated code to select messages and recipients. This is to
+ permit calling smtp_start_session() more than once on a given
+ session. Second and subsequent calls should only deliver to
+ recipients not successful in a previous SMTP session. Removed
+ a few FIXME comments that no longer apply.
+
+ * smtp-api.c
+ smtp_recipient_reset_status() clears the 'complete' flag so that
+ the recipient will be retried on a subsequent smtp_start_session().
+ Added a new API smtp_recipient_check_complete(). This is true if
+ a subsequent call to smtp_start_session() would *not* attempt to
+ post the message to this recipient.
+ smtp_destroy_session() now frees memory allocated for remote
+ server hostname.
+
+ * protocol.c libesmtp-private.h smtp-api.c
+ Renamed 'sent' in smtp_recipient_t structure to 'complete'.
+ Not all completed recipients might have been sent.
+
+ * protocol.c headers.c smtp-api.c
+ Only call gethostname() once and save the result. Also added
+ new API smtp_set_hostname() to allow the application to change
+ the default.
+
+2001-05-03 Brian Stafford <brian@stafford.uklinux.net>
+ * crammd5/Makefile.am crammd5/hmacmd5.[ch]
+ Renamed files to avoid name conflict with Cyrus SASL
+ include/ directory.
+
+ * base64.c message-source.c
+ Added missing #include <string.h>, egcs-2.91.66 didn't
+ spot the missing prototypes.
+
+ * examples/mail-file.c
+ Ignore SIGPIPE. Means the application isn't killed accidentally
+ when something times out during the protocol session.
+
+2001-05-02 Brian Stafford <brian@stafford.uklinux.net>
+ * sasl-tls.c protocol.c
+ Added experimental support for STARTTLS
+
+2001-04-31 Brian Stafford <brian@stafford.uklinux.net>
+ * cram-md5/Makefile.am
+ hmac-md5.h was missing from the list of sources and hence was
+ not in the tarball.
+
+2001-04-29 Version 0.6.1 released
+--------------------------------
+
+2001-04-30 Brian Stafford <brian@stafford.uklinux.net>
+ * auth-client.c
+ Fixed incorrect SSF comparison for authentication modules
+ that were already loaded.
+
+2001-04-29 Version 0.6a released
+--------------------------------
+
+2001-04-28 Brian Stafford <brian@stafford.uklinux.net>
+ * configure.in smtp-api.c example/mail-file.c
+ Corrected inconsistently named API from smtp_set_auth_context()
+ to smtp_auth_set_context().
+
+2001-04-25 Version 0.6 released
+--------------------------------
+
+2001-04-25 Brian Stafford <brian@stafford.uklinux.net>
+ * configure.in Makefile.am */Makefile.am
+ Added detection of MD5 routines in OpenSSL, to enable
+ the CRAM-MD5 SASL mechanism.
+
+2001-04-25 Brian Stafford <brian@stafford.uklinux.net>
+ * protocol.c
+ Corrected parsing bug in parse_status_triplet().
+
+2001-04-16 Brian Stafford <brian@stafford.uklinux.net>
+ * api.h
+ Added new header file. This currently contains macros
+ to aid argument checking for API functions.
+
+ * libesmtp.h
+ Changed name of API function argument check macro.
+
+ * most files
+ Wrapped #include <config.h> with #ifdef HAVE_CONFIG_H
+
+2001-04-11 Brian Stafford <brian@stafford.uklinux.net>
+ * rfc822date.c
+ Make sure the absolute value of minutes is used when
+ formatting the date.
+
+ * protocol.c
+ Fixed potential segfault when DATA fails before transferring
+ a message.
+
+ * smtp-auth.c
+ Support for client authentication plugins now working.
+
+ * protocol.c
+ Support for SMTP AUTH extension.
+
+2001-04-05 Brian Stafford <brian@stafford.uklinux.net>
+ * protocol.c smtp-auth.c
+ Removed 'want_enhanced' argument from read_smtp_response()
+ since it was unnecessary.
+ Added preliminary support for SMTP AUTH extension.
+
+2001-04-04 Brian Stafford <brian@stafford.uklinux.net>
+ * Many files
+ Changes to accomodate stricter error checking options
+ to gcc.
+
+2001-04-03 Brian Stafford <brian@stafford.uklinux.net>
+ * siobuf.c protocol.c
+ Changed CONFIG_TLS to USE_TLS (not that it matters yet)
+ Changed HAVE_LIBSASL to USE_SASL
+ Mostly for consistency with autoconf convention.
+
+ * configure.in
+ Checks for pthreads and SASL.
+
+2001-03-21 Brian Stafford <brian@stafford.uklinux.net>
+ * smtp-api.c
+ Fixed up some missing error reporting.
+
+2001-03-15 Brian Stafford <brian@stafford.uklinux.net>
+ * protocol.c
+ Now sets timeouts reccommended in RFC 1123 when waiting for
+ server responses.
+
+ * errors.c libesmtp.h
+ Changed prefix from ES_ to SMTP_ERR_. Edits to other files
+ to accommodate the change.
+
+2001-03-14 Brian Stafford <brian@stafford.uklinux.net>
+ * protocol.c libesmtp.h
+ Added first lot of event monitoring callbacks. Simplified the
+ declaration for the callback function. The callback is called
+ with different arguments depending on the actual event.
+
+2001-03-09 Brian Stafford <brian@stafford.uklinux.net>
+ * tokens.c tokens.h protocol.c
+ Added const to a few things that should have had it. Fixed a
+ corresponding declaration in protocol.c.
+
+ * smtp-auth.c
+ Basis of the implementation of the SMTP AUTH command. This is
+ not complete or tested yet, pending the decision about how to
+ best implement SASL.
+
+ * siobuf.c siobuf.h
+ Added callback functions which encode or decode data just before
+ writing or after reading data between the buffers and the
+ socket. This is for use by SASL security layers.
+
+2001-03-07 Brian Stafford <brian@stafford.uklinux.net>
+ * concatenate.c concatenate.h
+ Added minimum_length parameter to cat_{init,reset}().
+
+2001-03-07 Brian Stafford <brian@stafford.uklinux.net>
+ * siobuf.c siobuf.h protocol.c
+ Allow for seperate read and write file descriptors in
+ sio_attach(). This is for when support for opening a pipe to
+ a program running an SMTP server on its stdin/stdout is added.
+
+ * siobuf.c siobuf.h
+ Fixed the #ifdef _buffer_h lines to #ifdef _siobuf_h (the perils
+ of cut and paste editing).
+
+ Added typedefs for the sio callback functions.
+
+ Added encoder/decoder callbacks for use with security layer parts
+ of SASL. CONFIG_SASL stuff now gone.
+
+2001-03-02 Brian Stafford <brian@stafford.uklinux.net>
+ * headers.c
+ Implemented the rest of destroy_header_table().
+
+2001-02-27 Version 0.5 released
+--------------------------------
+
+2001-02-26 Brian Stafford <brian@stafford.uklinux.net>
+ * protocol.c
+ Now issue RSET before MAIL FROM: if a failure response is
+ received to the DATA command.
+
+ Fixed possible segfaults when resetting the status in rsp_rest()
+ and rsp_quit().
+
+ * headers.c
+ Partially implemented destroy_header_table().
+
+ * smtp-api.c
+ Now calls destroy_header_table().
+
+ * htable.c
+ Allow callback in h_destroy() to be NULL.
+
+2001-02-26 Version 0.4 released
+--------------------------------
+
+2001-02-25 Brian Stafford <brian@stafford.uklinux.net>
+ * protocol.c
+ Second state for the DATA command now does not transfer the
+ message if there were no valid recipients.
+
+2001-02-22 Brian Stafford <brian@stafford.uklinux.net>
+ * smtp-api.c
+ Added APIs to get/set application data in each of the opaque
+ structures.
+
+ Added protocol event callback API but only for a place holder.
+ This will be used by applications which want to monitor the
+ progress of the session and status changes as they happen.
+ This is different from the protocol monitor which dumps the
+ actual data transferred on or close to the wire.
+
+ * headers.c
+ Added code to handle Sender:
+
+ Fixed From: printing; continuation lines had no leading whitespace.
+
+ In smtp_set_header_option() once Hdr_PROHIBIT is set, it cannot
+ be unset. Prohibit cannot be set for headers already set.
+
+ Added smtp_set_resent_headers() but only for a place holder.
+
+ * protocol.c
+ Correct parsing of enhanced status codes. These are only present
+ for 2xx, 4xx and 5xx SMTP status codes.
+
+2001-02-18 Version 0.3 released
+--------------------------------
+
+2001-02-19 Brian Stafford <brian@stafford.uklinux.net>
+ * protocol.c smtp-api.c
+ Port number in session structure stored in host byte order
+ instead of network byte order. This makes the port number easier
+ to read in gdb.
+
+ * protocol.c
+ Removed white space which crept into the MAIL FROM: and RCPT TO:
+ commands. All the servers tested with to date have accepted
+ this but it wasn't in RFC 821.
+
+ * headers.c
+ Fixed From: and Disposition-Notification-To: headers to allow
+ multiple mailboxes as per RFC 822.
+
+ Corrected syntax for default Message-Id: generation. This
+ should have been "addr-spec" per RFC 822 but didn't have an @.
+
+ * examples/mail-file.c
+ Corrected typo that stopped --reverse-path from working.
+
+2001-02-18 Version 0.2 released
+--------------------------------
+ Core libESMTP API now complete.
+
+2001-02-18 Brian Stafford <brian@stafford.uklinux.net>
+ * examples/mail-file.c
+ Updated to tweak a few more APIs in libESMTP.
+ The example now has a very basic Makefile.
+
+2001-02-17 Brian Stafford <brian@stafford.uklinux.net>
+ * protocol.c
+ Changed use of strchr() to memchr() since strings read by the
+ message callback and header functions are *not* \0 terminated.
+
+ * headers.c
+ Changed beyond all recognition. :-)
+ Declaration of smtp_set_header_option() has changed.
+
+ * New files added to support RFC 822 header processing.
+
+2001-02-08 Brian Stafford <brian@stafford.uklinux.net>
+ * siobuf.c
+ Some additional error checking; extra thoroughness checking the
+ return value of write().
+
+ * siobuf.c
+ * protocol.c
+ Added the protocol monitor callback mechanism.
+
+ * libesmtp.h
+ * libesmtp-private.h
+ * smtp-api.c
+ Minor changes to the monitor callback declaration and session
+ structure for the protocol monitor.
+
+2001-02-04 Version 0.1a released
+--------------------------------
+
+2001-04-04 Brian Stafford <brian@stafford.uklinux.net>
+
+ * message-source.c
+ Fixed a bad bug that could cause an infinite loop if a message
+ was not properly terminated with a \n
+
+2001-02-04 Version 0.1 released
+-------------------------------
+
+2001-02-04 Brian Stafford <brian@stafford.uklinux.net>
+
+ * Initial Release
+
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..56b077d
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,236 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free
+Software Foundation, Inc.
+
+This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+These are generic installation instructions.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+ It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring. (Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.)
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+ The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'. You only need
+`configure.ac' if you want to change it or regenerate `configure' using
+a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system. If you're
+ using `csh' on an old version of System V, you might need to type
+ `sh ./configure' instead to prevent `csh' from trying to execute
+ `configure' itself.
+
+ Running `configure' takes awhile. While running, it prints some
+ messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+Compilers and Options
+=====================
+
+Some systems require unusual options for compilation or linking that the
+`configure' script does not know about. Run `./configure --help' for
+details on some of the pertinent environment variables.
+
+ You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment. Here
+is an example:
+
+ ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
+
+ *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ If you have to use a `make' that does not support the `VPATH'
+variable, you have to compile the package for one architecture at a
+time in the source code directory. After you have installed the
+package for one architecture, use `make distclean' before reconfiguring
+for another architecture.
+
+Installation Names
+==================
+
+By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc. You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PREFIX'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+give `configure' the option `--exec-prefix=PREFIX', the package will
+use PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+There may be some features `configure' cannot figure out automatically,
+but needs to determine by the type of machine the package will run on.
+Usually, assuming the package is built to be run on the _same_
+architectures, `configure' can figure that out, but if it prints a
+message saying it cannot guess the machine type, give it the
+`--build=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+ CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+ OS KERNEL-OS
+
+ See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+ If you are _building_ compiler tools for cross-compiling, you should
+use the `--target=TYPE' option to select the type of system they will
+produce code for.
+
+ If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+If you want to set default values for `configure' scripts to share, you
+can create a site shell script called `config.site' that gives default
+values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+Variables not defined in a site shell script can be set in the
+environment passed to `configure'. However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost. In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'. For example:
+
+ ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script). Here is a another example:
+
+ /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent
+configuration-related scripts to be executed by `/bin/bash'.
+
+`configure' Invocation
+======================
+
+`configure' recognizes the following options to control how it operates.
+
+`--help'
+`-h'
+ Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`--cache-file=FILE'
+ Enable the cache: use and save the results of the tests in FILE,
+ traditionally `config.cache'. FILE defaults to `/dev/null' to
+ disable caching.
+
+`--config-cache'
+`-C'
+ Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to `/dev/null' (any error
+ messages will still be shown).
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options. Run
+`configure --help' for more details.
+
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..bc60ef5
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,30 @@
+## Process this file with automake to produce Makefile.in
+AUTOMAKE_OPTIONS = gnu dist-bzip2
+
+INCLUDES = -I$(srcdir) $(VERSION_FLAGS)
+SUBDIRS = @subdirs@ @SASL_PLUGINS@
+DIST_SUBDIRS = @subdirs@ @DIST_PLUGINS@
+AM_CFLAGS = @CFLAGS@ @EXTRA_CFLAGS@
+
+lib_LTLIBRARIES = libesmtp.la
+
+libesmtp_la_SOURCES = auth-client.c base64.c concatenate.c errors.c \
+ headers.c htable.c message-source.c protocol.c \
+ rfc2822date.c siobuf.c smtp-api.c smtp-auth.c \
+ smtp-bdat.c smtp-tls.c smtp-etrn.c tokens.c \
+ api.h base64.h message-callbacks.c \
+ concatenate.h headers.h htable.h \
+ libesmtp-private.h message-source.h protocol-states.h \
+ protocol.h rfc2822date.h siobuf.h tokens.h \
+ getaddrinfo.h gethostbyname.h missing.h
+libesmtp_la_LIBADD = @LTLIBOBJS@
+
+libesmtp_la_LDFLAGS = -export-dynamic \
+ -version-info $(LIBESMTP_VERSION)
+
+include_HEADERS = libesmtp.h auth-client.h auth-plugin.h
+
+bin_SCRIPTS = libesmtp-config
+
+EXTRA_DIST = libesmtp.spec libesmtp.spec.in Notes doc/api.xml \
+ examples/Makefile examples/mail-file.c examples/test-mail
diff --git a/Makefile.in b/Makefile.in
new file mode 100644
index 0000000..3ab62eb
--- /dev/null
+++ b/Makefile.in
@@ -0,0 +1,828 @@
+# Makefile.in generated by automake 1.9.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+
+SOURCES = $(libesmtp_la_SOURCES)
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = .
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+DIST_COMMON = README $(am__configure_deps) $(include_HEADERS) \
+ $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
+ $(srcdir)/config.h.in $(srcdir)/libesmtp-config.in \
+ $(srcdir)/libesmtp.spec.in $(top_srcdir)/configure AUTHORS \
+ COPYING COPYING.LIB ChangeLog INSTALL NEWS TODO acconfig.h \
+ config.guess config.sub depcomp getaddrinfo.c gethostbyname.c \
+ install-sh ltmain.sh memrchr.c missing snprintf.c strcasecmp.c \
+ strdup.c strncasecmp.c
+subdir = .
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
+ configure.lineno configure.status.lineno
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = config.h
+CONFIG_CLEAN_FILES = libesmtp-config libesmtp.spec
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
+am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" \
+ "$(DESTDIR)$(includedir)"
+libLTLIBRARIES_INSTALL = $(INSTALL)
+LTLIBRARIES = $(lib_LTLIBRARIES)
+libesmtp_la_DEPENDENCIES = @LTLIBOBJS@
+am_libesmtp_la_OBJECTS = auth-client.lo base64.lo concatenate.lo \
+ errors.lo headers.lo htable.lo message-source.lo protocol.lo \
+ rfc2822date.lo siobuf.lo smtp-api.lo smtp-auth.lo smtp-bdat.lo \
+ smtp-tls.lo smtp-etrn.lo tokens.lo message-callbacks.lo
+libesmtp_la_OBJECTS = $(am_libesmtp_la_OBJECTS)
+binSCRIPT_INSTALL = $(INSTALL_SCRIPT)
+SCRIPTS = $(bin_SCRIPTS)
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I.
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(libesmtp_la_SOURCES)
+DIST_SOURCES = $(libesmtp_la_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
+ html-recursive info-recursive install-data-recursive \
+ install-exec-recursive install-info-recursive \
+ install-recursive installcheck-recursive installdirs-recursive \
+ pdf-recursive ps-recursive uninstall-info-recursive \
+ uninstall-recursive
+includeHEADERS_INSTALL = $(INSTALL_HEADER)
+HEADERS = $(include_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+distdir = $(PACKAGE)-$(VERSION)
+top_distdir = $(distdir)
+am__remove_distdir = \
+ { test ! -d $(distdir) \
+ || { find $(distdir) -type d ! -perm -200 -exec chmod u+w {} ';' \
+ && rm -fr $(distdir); }; }
+DIST_ARCHIVES = $(distdir).tar.gz $(distdir).tar.bz2
+GZIP_ENV = --best
+distuninstallcheck_listfiles = find . -type f -print
+distcleancheck_listfiles = find . -type f -print
+ACLOCAL = @ACLOCAL@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CRAMMD5_LIBS = @CRAMMD5_LIBS@
+CRAMMD5_LTLIBOBJS = @CRAMMD5_LTLIBOBJS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DIST_PLUGINS = @DIST_PLUGINS@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+EXTRA_CFLAGS = @EXTRA_CFLAGS@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBESMTP_VERSION = @LIBESMTP_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBTOOL_DEPS = @LIBTOOL_DEPS@
+LIB_AGE = @LIB_AGE@
+LIB_CURRENT = @LIB_CURRENT@
+LIB_REVISION = @LIB_REVISION@
+LN_S = @LN_S@
+LTALLOCA = @LTALLOCA@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+NTLM_LIBS = @NTLM_LIBS@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LDFLAGS = @PTHREAD_LDFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+RANLIB = @RANLIB@
+RPM_BUILDREQUIRES = @RPM_BUILDREQUIRES@
+RPM_OPENSSL = @RPM_OPENSSL@
+RPM_OPENSSLDEVEL = @RPM_OPENSSLDEVEL@
+RPM_REQUIRES = @RPM_REQUIRES@
+SASL_PLUGINS = @SASL_PLUGINS@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+ac_ct_RANLIB = @ac_ct_RANLIB@
+ac_ct_STRIP = @ac_ct_STRIP@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
+am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+plugindir = @plugindir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+subdirs = @subdirs@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+AUTOMAKE_OPTIONS = gnu dist-bzip2
+INCLUDES = -I$(srcdir) $(VERSION_FLAGS)
+SUBDIRS = @subdirs@ @SASL_PLUGINS@
+DIST_SUBDIRS = @subdirs@ @DIST_PLUGINS@
+AM_CFLAGS = @CFLAGS@ @EXTRA_CFLAGS@
+lib_LTLIBRARIES = libesmtp.la
+libesmtp_la_SOURCES = auth-client.c base64.c concatenate.c errors.c \
+ headers.c htable.c message-source.c protocol.c \
+ rfc2822date.c siobuf.c smtp-api.c smtp-auth.c \
+ smtp-bdat.c smtp-tls.c smtp-etrn.c tokens.c \
+ api.h base64.h message-callbacks.c \
+ concatenate.h headers.h htable.h \
+ libesmtp-private.h message-source.h protocol-states.h \
+ protocol.h rfc2822date.h siobuf.h tokens.h \
+ getaddrinfo.h gethostbyname.h missing.h
+
+libesmtp_la_LIBADD = @LTLIBOBJS@
+libesmtp_la_LDFLAGS = -export-dynamic \
+ -version-info $(LIBESMTP_VERSION)
+
+include_HEADERS = libesmtp.h auth-client.h auth-plugin.h
+bin_SCRIPTS = libesmtp-config
+EXTRA_DIST = libesmtp.spec libesmtp.spec.in Notes doc/api.xml \
+ examples/Makefile examples/mail-file.c examples/test-mail
+
+all: config.h
+ $(MAKE) $(AM_MAKEFLAGS) all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+am--refresh:
+ @:
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ echo ' cd $(srcdir) && $(AUTOMAKE) --gnu '; \
+ cd $(srcdir) && $(AUTOMAKE) --gnu \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnu Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ echo ' $(SHELL) ./config.status'; \
+ $(SHELL) ./config.status;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ $(SHELL) ./config.status --recheck
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(srcdir) && $(AUTOCONF)
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)
+
+config.h: stamp-h1
+ @if test ! -f $@; then \
+ rm -f stamp-h1; \
+ $(MAKE) stamp-h1; \
+ else :; fi
+
+stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
+ @rm -f stamp-h1
+ cd $(top_builddir) && $(SHELL) ./config.status config.h
+$(srcdir)/config.h.in: $(am__configure_deps) $(top_srcdir)/acconfig.h
+ cd $(top_srcdir) && $(AUTOHEADER)
+ rm -f stamp-h1
+ touch $@
+
+distclean-hdr:
+ -rm -f config.h stamp-h1
+libesmtp-config: $(top_builddir)/config.status $(srcdir)/libesmtp-config.in
+ cd $(top_builddir) && $(SHELL) ./config.status $@
+libesmtp.spec: $(top_builddir)/config.status $(srcdir)/libesmtp.spec.in
+ cd $(top_builddir) && $(SHELL) ./config.status $@
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(libdir)" || $(mkdir_p) "$(DESTDIR)$(libdir)"
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ if test -f $$p; then \
+ f=$(am__strip_dir) \
+ echo " $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(libdir)/$$f"; \
+ else :; fi; \
+ done
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @set -x; list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ p=$(am__strip_dir) \
+ echo " $(LIBTOOL) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$p'"; \
+ $(LIBTOOL) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$p"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+libesmtp.la: $(libesmtp_la_OBJECTS) $(libesmtp_la_DEPENDENCIES)
+ $(LINK) -rpath $(libdir) $(libesmtp_la_LDFLAGS) $(libesmtp_la_OBJECTS) $(libesmtp_la_LIBADD) $(LIBS)
+install-binSCRIPTS: $(bin_SCRIPTS)
+ @$(NORMAL_INSTALL)
+ test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)"
+ @list='$(bin_SCRIPTS)'; for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ if test -f $$d$$p; then \
+ f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
+ echo " $(binSCRIPT_INSTALL) '$$d$$p' '$(DESTDIR)$(bindir)/$$f'"; \
+ $(binSCRIPT_INSTALL) "$$d$$p" "$(DESTDIR)$(bindir)/$$f"; \
+ else :; fi; \
+ done
+
+uninstall-binSCRIPTS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_SCRIPTS)'; for p in $$list; do \
+ f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
+ echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \
+ rm -f "$(DESTDIR)$(bindir)/$$f"; \
+ done
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/getaddrinfo.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/gethostbyname.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/memrchr.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/snprintf.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strcasecmp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strdup.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strncasecmp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth-client.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base64.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/concatenate.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/errors.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/headers.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/htable.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/message-callbacks.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/message-source.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/protocol.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rfc2822date.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/siobuf.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smtp-api.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smtp-auth.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smtp-bdat.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smtp-etrn.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smtp-tls.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tokens.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ if $(LTCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+distclean-libtool:
+ -rm -f libtool
+uninstall-info-am:
+install-includeHEADERS: $(include_HEADERS)
+ @$(NORMAL_INSTALL)
+ test -z "$(includedir)" || $(mkdir_p) "$(DESTDIR)$(includedir)"
+ @list='$(include_HEADERS)'; for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ f=$(am__strip_dir) \
+ echo " $(includeHEADERS_INSTALL) '$$d$$p' '$(DESTDIR)$(includedir)/$$f'"; \
+ $(includeHEADERS_INSTALL) "$$d$$p" "$(DESTDIR)$(includedir)/$$f"; \
+ done
+
+uninstall-includeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(include_HEADERS)'; for p in $$list; do \
+ f=$(am__strip_dir) \
+ echo " rm -f '$(DESTDIR)$(includedir)/$$f'"; \
+ rm -f "$(DESTDIR)$(includedir)/$$f"; \
+ done
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run `make' without going through this Makefile.
+# To change the values of `make' variables: instead of editing Makefiles,
+# (1) if the variable is set in `config.status', edit `config.status'
+# (which will cause the Makefiles to be regenerated when you run `make');
+# (2) otherwise, pass the desired values on the `make' command line.
+$(RECURSIVE_TARGETS):
+ @failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+mostlyclean-recursive clean-recursive distclean-recursive \
+maintainer-clean-recursive:
+ @failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ rev=''; for subdir in $$list; do \
+ if test "$$subdir" = "."; then :; else \
+ rev="$$subdir $$rev"; \
+ fi; \
+ done; \
+ rev="$$rev ."; \
+ target=`echo $@ | sed s/-recursive//`; \
+ for subdir in $$rev; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done && test -z "$$fail"
+tags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+ done
+ctags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
+ done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: ctags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ $(am__remove_distdir)
+ mkdir $(distdir)
+ $(mkdir_p) $(distdir)/. $(distdir)/doc $(distdir)/examples
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+ list='$(DISTFILES)'; for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+ esac; \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkdir_p) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+ list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test -d "$(distdir)/$$subdir" \
+ || $(mkdir_p) "$(distdir)/$$subdir" \
+ || exit 1; \
+ distdir=`$(am__cd) $(distdir) && pwd`; \
+ top_distdir=`$(am__cd) $(top_distdir) && pwd`; \
+ (cd $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$top_distdir" \
+ distdir="$$distdir/$$subdir" \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+ -find $(distdir) -type d ! -perm -777 -exec chmod a+rwx {} \; -o \
+ ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
+ ! -type d ! -perm -400 -exec chmod a+r {} \; -o \
+ ! -type d ! -perm -444 -exec $(SHELL) $(install_sh) -c -m a+r {} {} \; \
+ || chmod -R a+r $(distdir)
+dist-gzip: distdir
+ tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
+ $(am__remove_distdir)
+dist-bzip2: distdir
+ tardir=$(distdir) && $(am__tar) | bzip2 -9 -c >$(distdir).tar.bz2
+ $(am__remove_distdir)
+
+dist-tarZ: distdir
+ tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
+ $(am__remove_distdir)
+
+dist-shar: distdir
+ shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz
+ $(am__remove_distdir)
+
+dist-zip: distdir
+ -rm -f $(distdir).zip
+ zip -rq $(distdir).zip $(distdir)
+ $(am__remove_distdir)
+
+dist dist-all: distdir
+ tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
+ tardir=$(distdir) && $(am__tar) | bzip2 -9 -c >$(distdir).tar.bz2
+ $(am__remove_distdir)
+
+# This target untars the dist file and tries a VPATH configuration. Then
+# it guarantees that the distribution is self-contained by making another
+# tarfile.
+distcheck: dist
+ case '$(DIST_ARCHIVES)' in \
+ *.tar.gz*) \
+ GZIP=$(GZIP_ENV) gunzip -c $(distdir).tar.gz | $(am__untar) ;;\
+ *.tar.bz2*) \
+ bunzip2 -c $(distdir).tar.bz2 | $(am__untar) ;;\
+ *.tar.Z*) \
+ uncompress -c $(distdir).tar.Z | $(am__untar) ;;\
+ *.shar.gz*) \
+ GZIP=$(GZIP_ENV) gunzip -c $(distdir).shar.gz | unshar ;;\
+ *.zip*) \
+ unzip $(distdir).zip ;;\
+ esac
+ chmod -R a-w $(distdir); chmod a+w $(distdir)
+ mkdir $(distdir)/_build
+ mkdir $(distdir)/_inst
+ chmod a-w $(distdir)
+ dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
+ && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \
+ && cd $(distdir)/_build \
+ && ../configure --srcdir=.. --prefix="$$dc_install_base" \
+ $(DISTCHECK_CONFIGURE_FLAGS) \
+ && $(MAKE) $(AM_MAKEFLAGS) \
+ && $(MAKE) $(AM_MAKEFLAGS) dvi \
+ && $(MAKE) $(AM_MAKEFLAGS) check \
+ && $(MAKE) $(AM_MAKEFLAGS) install \
+ && $(MAKE) $(AM_MAKEFLAGS) installcheck \
+ && $(MAKE) $(AM_MAKEFLAGS) uninstall \
+ && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \
+ distuninstallcheck \
+ && chmod -R a-w "$$dc_install_base" \
+ && ({ \
+ (cd ../.. && umask 077 && mkdir "$$dc_destdir") \
+ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \
+ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \
+ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \
+ distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \
+ } || { rm -rf "$$dc_destdir"; exit 1; }) \
+ && rm -rf "$$dc_destdir" \
+ && $(MAKE) $(AM_MAKEFLAGS) dist \
+ && rm -rf $(DIST_ARCHIVES) \
+ && $(MAKE) $(AM_MAKEFLAGS) distcleancheck
+ $(am__remove_distdir)
+ @(echo "$(distdir) archives ready for distribution: "; \
+ list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
+ sed -e '1{h;s/./=/g;p;x;}' -e '$${p;x;}'
+distuninstallcheck:
+ @cd $(distuninstallcheck_dir) \
+ && test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \
+ || { echo "ERROR: files left after uninstall:" ; \
+ if test -n "$(DESTDIR)"; then \
+ echo " (check DESTDIR support)"; \
+ fi ; \
+ $(distuninstallcheck_listfiles) ; \
+ exit 1; } >&2
+distcleancheck: distclean
+ @if test '$(srcdir)' = . ; then \
+ echo "ERROR: distcleancheck can only run from a VPATH build" ; \
+ exit 1 ; \
+ fi
+ @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \
+ || { echo "ERROR: files left in build directory after distclean:" ; \
+ $(distcleancheck_listfiles) ; \
+ exit 1; } >&2
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(LTLIBRARIES) $(SCRIPTS) $(HEADERS) config.h
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(includedir)"; do \
+ test -z "$$dir" || $(mkdir_p) "$$dir"; \
+ done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f $(am__CONFIG_DISTCLEAN_FILES)
+ -rm -rf $(DEPDIR) ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-hdr distclean-libtool distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+info: info-recursive
+
+info-am:
+
+install-data-am: install-includeHEADERS
+
+install-exec-am: install-binSCRIPTS install-libLTLIBRARIES
+
+install-info: install-info-recursive
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f $(am__CONFIG_DISTCLEAN_FILES)
+ -rm -rf $(top_srcdir)/autom4te.cache
+ -rm -rf $(DEPDIR) ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-binSCRIPTS uninstall-includeHEADERS \
+ uninstall-info-am uninstall-libLTLIBRARIES
+
+uninstall-info: uninstall-info-recursive
+
+.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am am--refresh check \
+ check-am clean clean-generic clean-libLTLIBRARIES \
+ clean-libtool clean-recursive ctags ctags-recursive dist \
+ dist-all dist-bzip2 dist-gzip dist-shar dist-tarZ dist-zip \
+ distcheck distclean distclean-compile distclean-generic \
+ distclean-hdr distclean-libtool distclean-recursive \
+ distclean-tags distcleancheck distdir distuninstallcheck dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-binSCRIPTS install-data install-data-am install-exec \
+ install-exec-am install-includeHEADERS install-info \
+ install-info-am install-libLTLIBRARIES install-man \
+ install-strip installcheck installcheck-am installdirs \
+ installdirs-am maintainer-clean maintainer-clean-generic \
+ maintainer-clean-recursive mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool mostlyclean-recursive \
+ pdf pdf-am ps ps-am tags tags-recursive uninstall uninstall-am \
+ uninstall-binSCRIPTS uninstall-includeHEADERS \
+ uninstall-info-am uninstall-libLTLIBRARIES
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..a9c108a
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,229 @@
+* libESMTP 1.0.3 stable release. 2004-04-20
+
+- This release contains TLS improvements from
+ Pawel Salek <pawsa@theochem.kth.se>
+ See ChangeLog for details.
+
+* libESMTP 1.0.2 stable release. 2004-01-06
+
+- See ChangeLog for details.
+
+* libESMTP 1.0.2 stable release. 2004-01-06
+
+- See ChangeLog for details.
+
+* libESMTP 1.0.1 stable release. 2003-09-12
+
+- See ChangeLog for details.
+
+* libESMTP 1.0 stable release. 2002-11-09
+
+ Tarball builds correctly again!
+
+- See ChangeLog for details.
+
+* libESMTP 1.0 stable release. 2002-11-09
+
+ LibESMTP is now considered stable. Version 1.0 is the best available
+ release of libESMTP and all users are urged to upgrade as soon as is
+ practicable.
+
+ There have been some minor changes to the configure script such that
+ ./configure with no arguments includes all non-experimental features.
+ This means that some features formerly not enabled by default are now
+ included and, conversely, some features formerly enabled by default must
+ now be requested explicitly. It is intended that, with the exception of
+ features such as setting --prefix or --with-gnu-ld, ./configure will
+ build the correct configuration for most OS distributions.
+
+- See ChangeLog for details.
+
+ This release fixes a minor compilation issue and a potentially more
+ serious memory reference after freeing.
+
+* libESMTP 1.0rc1 stable release candidate 1. 2002-06-24
+
+- See ChangeLog for details.
+
+ o Support for the non-standard AUTH= syntax used by some broken
+ servers is now on by default. This does not appear to interefere
+ with correctly implemented SMTP AUTH and having it on by default is
+ less confusing for users whose ISPs insist on deploying broken
+ servers.
+
+ o Added experimental support for RFC 3030 CHUNKING and BINARYMIME;
+ enable with ./configure --enable-chunking. Feedback on the success
+ or otherwise of this code is solicited.
+
+ o New API function to set protocol timeouts.
+
+* libESMTP 0.8.12 development release. 2002-04-24
+
+- See ChangeLog for details.
+
+ o Added missing check for STARTTLS if server does not support ESMTP.
+
+ o Revoked deprecated status from smtp_option_require_all_recipients
+
+* libESMTP 0.8.11 development release. 2002-03-06
+
+Fixed a buffer overflow which could be exploited by a malicious SMTP
+server. By overwriting the stack a carefully crafted response could
+cause arbitrary code to be executed.
+
+* libESMTP 0.8.10 development release. 2002-01-29
+
+- Usual autoconf stuff, see ChangeLog for details.
+
+Added an NTLM authentication module. Currently this requires OpenSSL to
+build. This has not seen much in the way of testing as I don't have
+regular access to a server which requires NTLM authentication for SMTP.
+However it does generate the correct responses for the test cases I have
+tried. Feedback on the success or otherwise of this module is solicited.
+
+Compilation with --enable-more-warnings=picky seems to be clean again.
+
+* libESMTP 0.8.9 development release. 2002-01-02
+
+- See ChangeLog for details.
+
+Important:
+ The use of libltdl is now deprecated in favour of dlopen(). libltdl
+ is no longer distributed with libESMTP reducing tarball size. This
+ change simplifies installation for the majority of users, however
+ users with platforms which do not supply dlopen or libltdl must now
+ obtain and install libltdl separately.
+
+Also Important:
+ Building with --enable-more-warnings=yes/picky might prove akward.
+ Recent glibc versions seem to have changed their mind about the
+ status of strcasecmp and friends to being GNU extensions.
+ Naturally, autoconf 2.13 detects the functions in the library but
+ not that their declarations are unavailable. For this reason,
+ _GNU_SOURCE is defined on gnu type platforms but this might cause
+ inconsistent pointer declarations wrt. signedness, YMMV. If you
+ have problems, try ./configure --disable-more-warnings.
+
+A horrible hack:
+ Added tentative support/hack for the non-standard AUTH= syntax in
+ EHLO responses. It might work. Don't complain to me if it doesn't.
+ You need to ./configure --enable-nsauth for this support.
+ This syntax was only ever described in internet drafts and never
+ made it into RFC 2554. It should *never* have been deployed on the
+ internet. Internet drafts are deleted after 6 months and after
+ publication of RFCs. So there is *no* documentation for this syntax
+ and I can't even begin to guess what it is supposed to be or what
+ implementation errors there are wrt these unavailable documents.
+ My advice is if this hack doesn't work, complain to your ISP and
+ recommend that they deploy MTAs that are standards compliant.
+ Documentation exists for standards and I am happy to make sure
+ libESMTP complies with documents I can actually obtain.
+
+* libESMTP 0.8.8 development release. 2001-11-30
+
+- See ChangeLog for details.
+
+ o Fixes more autoconf issues.
+
+ o Fixed a type mismatch that prevents compilation on some systems.
+
+* libESMTP 0.8.7 development release. 2001-11-7
+
+- See ChangeLog for details.
+
+ o Fixes minor build issues.
+
+ o Improved error handling wrt getaddrinfo
+
+* libESMTP 0.8.6 development release. 2001-10-17
+
+- See ChangeLog for details.
+
+ o Fixes minor build issues.
+
+ o SASL CRAM-MD5 builds without OpenSSL
+
+* libESMTP 0.8.5 development release. 2001-10-04
+
+- See ChangeLog for details.
+
+ o Header code no longer enforces presence of recipient fields.
+
+ o Fixed some build issues related to the automake/libtool interaction.
+ Reverted to autoconf 2.13
+
+ o Removed support for gethostbyname resolver interface. Please
+ refer to the 'Dependencies' section in README.
+
+ o Enhancements to STARTTLS support.
+
+ o Calculation of current timezone's offset from GMT (UTC) is now
+ portable and thread safe.
+
+* libESMTP 0.8.4 development release. 2001-08-13
+
+- See ChangeLog for details.
+
+* libESMTP 0.8.3 development release. 2001-07-06
+
+- See ChangeLog for details.
+
+ o Support for sendmail's XUSR extension.
+
+ o Fixed a bad bug which caused connections to the server to be dropped
+ depending on the amount of buffering provided by the server.
+
+* libESMTP 0.8.2 development release. 2001-06-26
+
+- See ChangeLog for details.
+
+ o Added lots of assertions in the code.
+
+ o Fixed a bad dangling pointer bug that could strike when sending
+ messages with lines > 510 characters.
+
+ o Fixed a polling bug that could cause deadlock.
+
+ o Resolver interface now uses Posix standard getaddrinfo.
+ Use of gethostbyname is deprecated.
+
+Please note that the current RFC 2822 header API is adequate but
+incomplete; for example, interactions between certain headers are not
+implemented. This will not change for a while. The current priority is
+to make the protocol engine robust.
+
+* libESMTP 0.8.1 development release. 2001-06-15
+
+- See ChangeLog for details.
+
+Fixed two uninitialised variable bugs that might cause the protocol
+to quit without sending anything to the server.
+
+Enabled many more compiler warnings when compiling with gcc. Compiles
+should now be much cleaner.
+
+* libESMTP 0.8.0 development release. 2001-06-12
+
+- See ChangeLog for details.
+
+The libESMTP feature set and API for version 1.0 is more or less complete.
+There have been minor changes to the arguments or semantics of some of
+the API functions, particularly wrt. the callback functions. Applications
+using previous libESMTP versions will need to be recompiled or relinked.
+
+From this point on no new features will be added and, as far as possible,
+API changes will be resisted. Having said that, the range of error codes
+will likely be expanded. Effort will now be directed at bug fixes and
+improving the documentation and web site, though this is likely to be a
+slow process.
+
+Many of the supported SMTP extensions have had only superficial testing
+mainly due to lack of access to servers supporting them. Developers using
+libESMTP are encouraged to test extensions against servers to which they
+have access and to submit bug reports to <brian@stafford.uklinux.net>.
+
+The libESMTP web site will be updated in the near future to set up
+(finally!) mailing lists and bug tracking. In addition the web site will
+link to projects using libESMTP. If you would like a mention for your
+project, drop a line to <brian@stafford.uklinux.net> with the details.
+
diff --git a/Notes b/Notes
new file mode 100644
index 0000000..3eb1245
--- /dev/null
+++ b/Notes
@@ -0,0 +1,95 @@
+Memory Allocation & Leaks
+=========================
+
+Investigate and fix leaks.
+More thorough checking of malloc failures. Where possible fail functions
+that check malloc but don't check a subsequent strdup.
+
+Threads
+=======
+
+Add mutexes to allow multiple threads to access data structures where
+reasonable. It would make sense for smtp_start_session() to be able to
+run in a different thread from the rest of the program. Having said that,
+libESMTP should be safe in a multithreaded program so long as only one
+thread accesses one session_t and its children at a time. There are no
+global variables so different threads could work on different sessions
+simultaneously.
+
+Headers
+=======
+
+When a header is set and is overriding a header in the message, make sure
+a value set in the API is used only once. For example the api defines
+"X-My-Header: Hello World" but the message has two instances of X-My-Header:
+The resulting message should have only one X-My-Header.
+
+Implement Disposition-Notification-Options: not very urgent.
+
+Sender: is required if there are multiple values to From:
+
+Quoting
+=======
+
+In concatenate.c: Add a version of concatentate() that takes an extra argument
+which determines the quoting rules for the string being copied to the buffer.
+At present, the application must be careful to supply only characters which are
+legal when not quoted which is obviously v. unsatisfactory.
+
+When supplying a phrase or a mailbox, different quoting conventions apply
+do different syntatic elements of the phrase and maibox. A better solution
+might be to provide new APIs which quote and combines individual syntatic
+elements into a single string which is then passed to the API.
+
+Auth
+====
+
+Need to support client side for
+ EXTERNAL RFC 2222 sect 7.4 (done - untested)
+ CRAM-MD5 RFC 2195 (done)
+ PLAIN RFC 2595 (done)
+ LOGIN undocumented (done)
+ ANONYMOUS RFC 2245
+ OTP RFC 2444
+ SecurID RFC 2808
+ DIGEST-MD5 RFC 2831
+ Kerberos RFC 2222 sect 7.1
+ GSSAPI RFC 2222 sect 7.2
+
+TLS/SSL
+=======
+
+Since S/MIME or PGP/MIME is the only real way to protect the message,
+what does STARTTLS achieve? No real point authenticating the client
+to the server in general mail relay.
+
+Mail submission: is a client certificate useful?
+ Client certificate provides authentication in conjunction with AUTH
+ (EXTERNAL) or as an alternative.
+
+Server certificate: should the client verify the server certificate or
+the CA? What is gained by doing so?
+ Certainty that mail is submitted via an organisation's MTA. Important
+ if disclaimers / signing etc. is done.
+
+S/MIME will protect the message content. TLS will protect the envelope.
+
+Provides protection against passive attack.
+
+Actually, for the case of mail submission, TLS might be quite useful.
+Consider the case of someone from an organisation who is out on the road.
+The only MTA they might have access to that will accept mail for relay
+is their own organisation's submission server. This first hop may pass
+a significant number of routers which could potentially eavesdrop on
+the message. The submission server might be responsible for signing
+and encrypting messages on the grounds that it is normally accepting
+connections from behind a firewall. This is feasible using S/MIME or
+PGP MIME. Without encrypting the first hop, this confidentiality measure
+is lost. Client certificate is likely to be required in this scenario too.
+
+Auth + TLS
+==========
+
+Really need a mechanism to manage authentication tokens, e.g. SASL user/pw
+combos or X.509 certs/private keys.
+
diff --git a/README b/README
new file mode 100644
index 0000000..4dfd4a9
--- /dev/null
+++ b/README
@@ -0,0 +1,106 @@
+ libESMTP, version 1.0
+ -- oOo --
+ Brian Stafford <brian@stafford.uklinux.net>
+
+
+What is libESMTP?
+-----------------
+
+LibESMTP is a library to manage posting (or submission of) electronic
+mail using SMTP to a preconfigured Mail Transport Agent (MTA) such as
+Exim or Postfix. It may be used as part of a Mail User Agent (MUA) or
+another program that must be able to post electronic mail but where mail
+functionality is not the program's primary purpose.
+
+LibESMTP is not intended to be used as part of a program that implements
+a Mail Transport Agent.
+
+It is hoped that the availability of a lightweight library implementing
+an SMTP client will both ease the task of coding for software authors
+and improve the quality of the resulting code.
+
+Features
+--------
+
+Support for many SMTP extensions, notably PIPELINING (RFC 2920),
+DSN (RFC 2554) and AUTH (RFC 2554). Also supported is the
+sendmail specific XUSR extension which informs sendmail that the
+message is an initial submission.
+
+SASL
+----
+
+AUTH is implemented using a SASL (RFC 2222) client library which is
+currently integrated into libESMTP. It was felt that the Cyrus SASL
+library was too complex for the needs of a client only SASL
+implementation.
+
+If there is sufficient interest in a LGPL SASL library, the SASL client
+API will be split off into a separate library in the future. There may
+also be a case for implementing a server side SASL library along the
+same lines as the client implementation.
+
+Installation
+------------
+
+Please refer to INSTALL for generic installation instructions. LibESMTP
+has a few options when configuring; ./configure --help lists them.
+
+Dependencies
+------------
+
+dlsym:
+
+libESMTP requires that dlsym() is available on your system. This is
+true of many modern systems but not all. An alternative is to download
+and install libltdl which provides a functional equivalent. Libltdl is
+distributed with GNU Libtool, which is available from
+http://www.gnu.org/software/libtool/
+
+getaddrinfo:
+
+You will need a modern resolver library providing the getaddrinfo API.
+getaddrinfo is easier to use, protocol independent, thread-safe and
+RFC 2553 and Posix standard.
+
+An emulation of this is provided for systems that do not have it, however
+it is reccommended that the version provided in recent versions of GNU
+libc or BIND is used. Most people will already at least one of these
+(e.g. virtually every Linux distro). There is also support for the
+lightweight resolver distributed with BIND 9. BIND may be downloaded
+from the ISC (http://www.isc.org/).
+
+openssl:
+
+OpenSSL (http://www.openssl.org/) is required to build the SMTP STARTTLS
+extension and the NTLM authentication module. If you have no need for
+either of these features, you do not need OpenSSL.
+
+
+Licence
+-------
+
+LibESMTP is licensed under the GNU Lesser General Public License and the
+example programs are under the GNU General Public Licence. Please refer
+to COPYING.GPL and COPYING for full details.
+
+Obtaining libESMTP
+------------------
+
+LibESMTP may be obtained from:
+ http://www.stafford.uklinux.net/libesmtp/
+
+Documentation
+-------------
+
+LibESMTP documentation is available on the web at:
+ http://www.stafford.uklinux.net/libesmtp/api.html
+This probably (definitely) lags behind the actual source code.
+
+What does the 'E' stand for?
+--------------------------
+
+The 'E' in libESMTP is there because support for a number of SMTP
+extensions is built in to the library by design.
+
+
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..f8c6165
--- /dev/null
+++ b/TODO
@@ -0,0 +1,31 @@
+ o Finish the TLS code.
+ * report server certificate details (esp fingerprint) to application
+ * report cipher in use to application.
+ * API to specify minimum acceptable security levels.
+
+ o SASL
+ * New plugin strategy. If the server lists acceptable mechanisms
+ load them all. If not load all plugins. Eliminate those which
+ cannot negotiate a sufficient security level. If encryption
+ is already in use, eliminate those which *must* encrypt. Rank
+ remaining mechanisms according to the protection afforded to the
+ username and password. Attempt to authenticate using highest rank
+ mechanism to lowest. Special case: if the server offers EXTERNAL
+ and the external token has been set, use that as the highest
+ ranking mechanism. If the server refuses a mechanism, back off to
+ the next mechanism. If the server accepts the mechanism but fails
+ authentication, end the sequence. Special exception: if EXTERNAL
+ was used and authentication fails, back off to the next mechanism.
+
+ o Make header code do line folding at white spaces.
+
+ o Make header code handle Resent-* headers.
+
+ o Make header code handle list notation in appropriate recipient headers.
+
+ o Review API.
+
+ o Review error reporting.
+
+ o Loadsa documentation.
+
diff --git a/api.h b/api.h
new file mode 100644
index 0000000..0f2fe13
--- /dev/null
+++ b/api.h
@@ -0,0 +1,35 @@
+#ifndef _api_h
+#define _api_h
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* This file collates stuff needed by modules defining API functions.
+ */
+
+#define API_CHECK_ARGS(test,ret) \
+ do \
+ if (!(test)) { \
+ return ret; \
+ } \
+ while (0)
+
+#endif
diff --git a/auth-client.c b/auth-client.c
new file mode 100644
index 0000000..c56ffee
--- /dev/null
+++ b/auth-client.c
@@ -0,0 +1,471 @@
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+
+#ifdef USE_PTHREADS
+#include <pthread.h>
+#endif
+
+#if HAVE_DLSYM
+# include <dlfcn.h>
+# ifndef DLEXT
+# define DLEXT ".so"
+# endif
+# ifndef RTLD_LAZY
+# define RTLD_LAZY RTLD_NOW
+# endif
+typedef void *dlhandle_t;
+#else
+# include <ltdl.h>
+# ifndef DLEXT
+# define DLEXT ""
+# endif
+typedef lt_dlhandle dlhandle_t;
+# define dlopen(n,f) lt_dlopenext((n))
+# define dlsym(h,s) lt_dlsym((h),(s))
+# define dlclose(h) lt_dlclose((h))
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <missing.h>
+
+#include "auth-client.h"
+#include "auth-plugin.h"
+#include "api.h"
+
+#ifdef USE_PTHREADS
+static pthread_mutex_t plugin_mutex = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+struct auth_plugin
+ {
+ struct auth_plugin *next;
+ dlhandle_t module;
+ const struct auth_client_plugin *info;
+ };
+static struct auth_plugin *client_plugins, *end_client_plugins;
+
+struct auth_context
+ {
+ int min_ssf;
+ unsigned flags;
+ const struct auth_client_plugin *client;
+ void *plugin_ctx;
+ auth_interact_t interact;
+ void *interact_arg;
+ char *external_id;
+ };
+
+#define mechanism_disabled(p,a,f) \
+ (((p)->flags & AUTH_PLUGIN_##f) && !((a)->flags & AUTH_PLUGIN_##f))
+
+#if HAVE_DLSYM && defined AUTHPLUGINDIR
+# define PLUGIN_DIR AUTHPLUGINDIR "/"
+#else
+# define PLUGIN_DIR
+#endif
+
+static char *
+plugin_name (const char *str)
+{
+ char *buf, *p;
+ static const char prefix[] = PLUGIN_DIR "sasl-";
+
+ assert (str != NULL);
+
+ buf = malloc (sizeof prefix + strlen (str) + sizeof DLEXT);
+ if (buf == NULL)
+ return NULL;
+ strcpy (buf, prefix);
+ p = buf + sizeof prefix - 1;
+ while (*str != '\0')
+ *p++ = tolower (*str++);
+ strcpy (p, DLEXT);
+ return buf;
+}
+
+static int
+append_plugin (dlhandle_t module, const struct auth_client_plugin *info)
+{
+ struct auth_plugin *auth_plugin;
+
+ assert (info != NULL);
+
+ auth_plugin = malloc (sizeof (struct auth_plugin));
+ if (auth_plugin == NULL)
+ {
+ /* FIXME: propagate an ENOMEM back to the app. */
+ return 0;
+ }
+ auth_plugin->info = info;
+ auth_plugin->module = module;
+ auth_plugin->next = NULL;
+ if (client_plugins == NULL)
+ client_plugins = auth_plugin;
+ else
+ end_client_plugins->next = auth_plugin;
+ end_client_plugins = auth_plugin;
+ return 1;
+}
+
+static const struct auth_client_plugin *
+load_client_plugin (const char *name)
+{
+ dlhandle_t module;
+ char *plugin;
+ const struct auth_client_plugin *info;
+
+ assert (name != NULL);
+
+ /* Try searching for a plugin. */
+ plugin = plugin_name (name);
+ if (plugin == NULL)
+ return NULL;
+ module = dlopen (plugin, RTLD_LAZY);
+ free (plugin);
+ if (module == NULL)
+ return NULL;
+
+ info = dlsym (module, "sasl_client");
+ if (info == NULL || info->response == NULL)
+ {
+ dlclose (module);
+ return NULL;
+ }
+
+ /* This plugin module is OK. Add it to the list of loaded modules.
+ */
+ if (!append_plugin (module, info))
+ {
+ dlclose (module);
+ return NULL;
+ }
+
+ return info;
+}
+
+void
+auth_client_init (void)
+{
+#if !HAVE_DLSYM
+# ifdef USE_PTHREADS
+ pthread_mutex_lock (&plugin_mutex);
+# endif
+ lt_dlinit ();
+# ifdef AUTHPLUGINDIR
+ lt_dladdsearchdir (AUTHPLUGINDIR);
+# endif
+ /* Add builtin mechanisms to the plugin list */
+# ifdef USE_PTHREADS
+ pthread_mutex_unlock (&plugin_mutex);
+# endif
+#endif
+}
+
+void
+auth_client_exit (void)
+{
+ struct auth_plugin *plugin, *next;
+
+ /* Scan the auth_plugin array and dlclose() the modules */
+#ifdef USE_PTHREADS
+ pthread_mutex_lock (&plugin_mutex);
+#endif
+ for (plugin = client_plugins; plugin != NULL; plugin = next)
+ {
+ next = plugin->next;
+ if (plugin->module != NULL)
+ dlclose (plugin->module);
+ free (plugin);
+ }
+ client_plugins = end_client_plugins = NULL;
+#if !HAVE_DLSYM
+ lt_dlexit ();
+#endif
+#ifdef USE_PTHREADS
+ pthread_mutex_unlock (&plugin_mutex);
+#endif
+}
+
+auth_context_t
+auth_create_context (void)
+{
+ auth_context_t context;
+
+ context = malloc (sizeof (struct auth_context));
+ if (context == NULL)
+ return NULL;
+
+ memset (context, 0, sizeof (struct auth_context));
+ return context;
+}
+
+int
+auth_destroy_context (auth_context_t context)
+{
+ API_CHECK_ARGS (context != NULL, 0);
+
+ if (context->plugin_ctx != NULL)
+ {
+ if (context->client != NULL && context->client->destroy != NULL)
+ (*context->client->destroy) (context->plugin_ctx);
+ }
+ free (context);
+ return 1;
+}
+
+int
+auth_set_mechanism_flags (auth_context_t context, unsigned set, unsigned clear)
+{
+ API_CHECK_ARGS (context != NULL, 0);
+
+ context->flags &= AUTH_PLUGIN_EXTERNAL | ~clear;
+ context->flags |= ~AUTH_PLUGIN_EXTERNAL & set;
+ return 1;
+}
+
+int
+auth_set_mechanism_ssf (auth_context_t context, int min_ssf)
+{
+ API_CHECK_ARGS (context != NULL, 0);
+
+ context->min_ssf = min_ssf;
+ return 1;
+}
+
+int
+auth_set_external_id (auth_context_t context, const char *identity)
+{
+ static const struct auth_client_plugin external_client =
+ {
+ /* Plugin information */
+ "EXTERNAL",
+ "SASL EXTERNAL mechanism (RFC 2222)",
+ /* Plugin instance */
+ NULL,
+ NULL,
+ /* Authentication */
+ (auth_response_t) 1,
+ AUTH_PLUGIN_EXTERNAL,
+ /* Security Layer */
+ 0,
+ NULL,
+ NULL,
+ };
+ struct auth_plugin *plugin;
+
+ API_CHECK_ARGS (context != NULL, 0);
+
+ if (context->external_id != NULL)
+ free (context->external_id);
+ if (identity != NULL)
+ {
+ /* Install external module if required */
+ for (plugin = client_plugins; plugin != NULL; plugin = plugin->next)
+ if (plugin->info->flags & AUTH_PLUGIN_EXTERNAL)
+ break;
+ if (plugin == NULL)
+ {
+#ifdef USE_PTHREADS
+ pthread_mutex_lock (&plugin_mutex);
+#endif
+ append_plugin (NULL, &external_client);
+#ifdef USE_PTHREADS
+ pthread_mutex_unlock (&plugin_mutex);
+#endif
+ }
+ context->flags |= AUTH_PLUGIN_EXTERNAL;
+ context->external_id = strdup (identity);
+ }
+ else
+ {
+ context->flags &= ~AUTH_PLUGIN_EXTERNAL;
+ context->external_id = NULL;
+ }
+ return 1;
+}
+
+int
+auth_set_interact_cb (auth_context_t context,
+ auth_interact_t interact, void *arg)
+{
+ API_CHECK_ARGS (context != NULL, 0);
+
+ context->interact = interact;
+ context->interact_arg = arg;
+ return 1;
+}
+
+/* Perform various checks to see if SASL is usable. Do not check
+ for loaded plugins though. This is checked when trying to
+ select one for use. */
+int
+auth_client_enabled (auth_context_t context)
+{
+ if (context == NULL)
+ return 0;
+ if (context->interact == NULL)
+ return 0;
+ return 1;
+}
+
+int
+auth_set_mechanism (auth_context_t context, const char *name)
+{
+ struct auth_plugin *plugin;
+ const struct auth_client_plugin *info;
+
+ API_CHECK_ARGS (context != NULL && name != NULL, 0);
+
+#ifdef USE_PTHREADS
+ pthread_mutex_lock (&plugin_mutex);
+#endif
+
+ /* Get rid of old context */
+ if (context->plugin_ctx != NULL)
+ {
+ if (context->client != NULL && context->client->destroy != NULL)
+ (*context->client->destroy) (context->plugin_ctx);
+ context->plugin_ctx = NULL;
+ }
+
+ /* Check the list of already loaded modules. */
+ info = NULL;
+ for (plugin = client_plugins; plugin != NULL; plugin = plugin->next)
+ if (strcasecmp (name, plugin->info->keyw) == 0)
+ {
+ info = plugin->info;
+ break;
+ }
+
+ /* Load the module if not found above. */
+ if (info == NULL && (info = load_client_plugin (name)) == NULL)
+ {
+#ifdef USE_PTHREADS
+ pthread_mutex_unlock (&plugin_mutex);
+#endif
+ return 0;
+ }
+
+ /* Check the application's requirements regarding minimum SSF and
+ whether anonymous or plain text mechanisms are explicitly allowed.
+ This has to be checked here since the list of loaded plugins is
+ global and the plugin may have been acceptable in another context
+ but not in this one. */
+ if (info->ssf < context->min_ssf
+ || mechanism_disabled (info, context, EXTERNAL)
+ || mechanism_disabled (info, context, ANONYMOUS)
+ || mechanism_disabled (info, context, PLAIN))
+ {
+#ifdef USE_PTHREADS
+ pthread_mutex_unlock (&plugin_mutex);
+#endif
+ return 0;
+ }
+
+ context->client = info;
+#ifdef USE_PTHREADS
+ pthread_mutex_unlock (&plugin_mutex);
+#endif
+ return 1;
+}
+
+const char *
+auth_mechanism_name (auth_context_t context)
+{
+ API_CHECK_ARGS (context != NULL && context->client != NULL, NULL);
+
+ return context->client->keyw;
+}
+
+const char *
+auth_response (auth_context_t context, const char *challenge, int *len)
+{
+ API_CHECK_ARGS (context != NULL
+ && context->client != NULL
+ && len != NULL
+ && ((context->client->flags & AUTH_PLUGIN_EXTERNAL)
+ || context->interact != NULL),
+ NULL);
+
+ if (challenge == NULL)
+ {
+ if (context->plugin_ctx != NULL && context->client->destroy != NULL)
+ (*context->client->destroy) (context->plugin_ctx);
+ if (context->client->init == NULL)
+ context->plugin_ctx = NULL;
+ else if (!(*context->client->init) (&context->plugin_ctx))
+ return NULL;
+ }
+ if (context->client->flags & AUTH_PLUGIN_EXTERNAL)
+ {
+ *len = strlen (context->external_id);
+ return context->external_id;
+ }
+ assert (context->client->response != NULL);
+ return (*context->client->response) (context->plugin_ctx,
+ challenge, len,
+ context->interact,
+ context->interact_arg);
+}
+
+int
+auth_get_ssf (auth_context_t context)
+{
+ API_CHECK_ARGS (context != NULL && context->client != NULL, -1);
+
+ return context->client->ssf;
+}
+
+void
+auth_encode (char **dstbuf, int *dstlen,
+ const char *srcbuf, int srclen, void *arg)
+{
+ auth_context_t context = arg;
+
+ if (context != NULL
+ && context->client != NULL
+ && context->client->encode != NULL)
+ (*context->client->encode) (context->plugin_ctx,
+ dstbuf, dstlen, srcbuf, srclen);
+}
+
+void
+auth_decode (char **dstbuf, int *dstlen,
+ const char *srcbuf, int srclen, void *arg)
+{
+ auth_context_t context = arg;
+
+ if (context != NULL
+ && context->client != NULL
+ && context->client->encode != NULL)
+ (*context->client->decode) (context->plugin_ctx,
+ dstbuf, dstlen, srcbuf, srclen);
+}
diff --git a/auth-client.h b/auth-client.h
new file mode 100644
index 0000000..55c0554
--- /dev/null
+++ b/auth-client.h
@@ -0,0 +1,91 @@
+#ifndef _auth_client_h
+#define _auth_client_h
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Someday this will expand into a standalone SASL library. For now the
+ application must use the remainder of the SASL implementation's API
+ to set things up before calling smtp_start_session(). */
+
+typedef struct auth_context *auth_context_t;
+
+/* These flags are set for standard user authentication info */
+#define AUTH_USER 0x0001
+#define AUTH_REALM 0x0002
+#define AUTH_PASS 0x0004
+
+/* This flag is set for information passed in clear text on the wire */
+#define AUTH_CLEARTEXT 0x0008
+
+struct auth_client_request
+ {
+ const char *name; /* Name of field requested from the application,
+ e.g. "user", "passphrase" "realm" etc. */
+ unsigned flags; /* Alternative version of above */
+ const char *prompt; /* Text that the application can use to prompt
+ for input. */
+ unsigned size; /* Maximum length of response allowed. 0 == no limit */
+ };
+typedef const struct auth_client_request *auth_client_request_t;
+typedef int (*auth_interact_t) (auth_client_request_t request,
+ char **result, int fields, void *arg);
+typedef const char *(*auth_response_t) (void *ctx,
+ const char *challenge, int *len,
+ auth_interact_t interact, void *arg);
+
+typedef int (*auth_recode_t) (void *ctx, char **dstbuf, int *dstlen,
+ const char *srcbuf, int srclen);
+
+/* For enabling mechanisms */
+#define AUTH_PLUGIN_ANONYMOUS 0x01 /* mechanism is anonymous */
+#define AUTH_PLUGIN_PLAIN 0x02 /* mechanism uses plaintext passwords */
+#define AUTH_PLUGIN_EXTERNAL 0x04 /* mechanism is EXTERNAL */
+
+
+void auth_client_init(void);
+void auth_client_exit(void);
+auth_context_t auth_create_context(void);
+int auth_destroy_context(auth_context_t context);
+int auth_set_mechanism_flags (auth_context_t context,
+ unsigned set, unsigned clear);
+int auth_set_mechanism_ssf (auth_context_t context, int min_ssf);
+int auth_set_interact_cb (auth_context_t context,
+ auth_interact_t interact, void *arg);
+int auth_client_enabled(auth_context_t context);
+int auth_set_mechanism(auth_context_t context, const char *name);
+const char *auth_mechanism_name (auth_context_t context);
+const char *auth_response(auth_context_t context, const char *challenge, int *len);
+int auth_get_ssf(auth_context_t context);
+void auth_encode(char **dstbuf, int *dstlen, const char *srcbuf, int srclen, void *arg);
+void auth_decode(char **dstbuf, int *dstlen, const char *srcbuf, int srclen, void *arg);
+int auth_set_external_id (auth_context_t context, const char *identity);
+
+#ifdef __cplusplus
+};
+#endif
+
+
+#endif
diff --git a/auth-plugin.h b/auth-plugin.h
new file mode 100644
index 0000000..7b7989e
--- /dev/null
+++ b/auth-plugin.h
@@ -0,0 +1,56 @@
+#ifndef _auth_plugin_h
+#define _auth_plugin_h
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/****************************************************************************
+ * Client side SASL plugins.
+ * This header is used only by the plugins. Structures and constants shared
+ * between the plugin and the app are in auth-client.h
+ ****************************************************************************/
+
+typedef int (*auth_init_client_t) (void *pctx);
+typedef void (*auth_detsroy_client_t) (void *ctx);
+
+/* Plugins export client info through this structure */
+struct auth_client_plugin
+ {
+ /* Plugin information */
+ const char *keyw; /* mechanism keyword */
+ const char *description; /* description of the mechanism */
+ /* Plugin instance */
+ auth_init_client_t init; /* Create plugin context */
+ auth_detsroy_client_t destroy; /* Destroy plugin context */
+ /* Authentication */
+ auth_response_t response; /* request response to a challenge */
+ int flags; /* plugin information */
+ /* Security Layer */
+ int ssf; /* security strength */
+ auth_recode_t encode; /* encode data for transmission */
+ auth_recode_t decode; /* decode received data */
+ };
+
+#ifndef NULL
+# define NULL ((void *) 0)
+#endif
+
+#endif
diff --git a/base64.c b/base64.c
new file mode 100644
index 0000000..769a3be
--- /dev/null
+++ b/base64.c
@@ -0,0 +1,167 @@
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+
+/* Routines to encode and decode base64 text.
+ */
+#include <ctype.h>
+#include <string.h>
+#include "base64.h"
+
+/* RFC 2045 section 6.8 */
+
+static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789"
+ "+/";
+
+static const char index_64[128] =
+ {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
+ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
+ };
+
+/* Decode srclen bytes of base64 data contained in src and put the result
+ in dst. Since the destination buffer may contain arbitrary binary
+ data and it is not necessarily a string, there is no \0 byte at the
+ end of the decoded data. */
+int
+b64_decode (void *dst, int dstlen, const char *src, int srclen)
+{
+ const unsigned char *p, *q;
+ unsigned char *t;
+ int c1, c2;
+
+ assert (dst != NULL && dstlen > 0);
+
+ if (src == NULL)
+ return 0;
+
+ if (srclen < 0)
+ srclen = strlen (src);
+
+ /* Remove leading and trailing white space */
+ for (p = (const unsigned char *) src;
+ srclen > 0 && isspace (*p);
+ p++, srclen--)
+ ;
+ for (q = p + srclen - 1; q >= p && isspace (*q); q--, srclen--)
+ ;
+
+ /* Length MUST be a multiple of 4 */
+ if (srclen % 4 != 0)
+ return -1;
+
+ /* Destination buffer length must be sufficient */
+ if (srclen / 4 * 3 + 1 > dstlen)
+ return -1;
+
+ t = dst;
+ while (srclen > 0)
+ {
+ srclen -= 4;
+ if (*p >= 128 || (c1 = index_64[*p++]) == -1)
+ return -1;
+ if (*p >= 128 || (c2 = index_64[*p++]) == -1)
+ return -1;
+ *t++ = (c1 << 2) | ((c2 & 0x30) >> 4);
+
+ if (p[0] == '=' && p[1] == '=')
+ break;
+ if (*p >= 128 || (c1 = index_64[*p++]) == -1)
+ return -1;
+ *t++ = ((c2 & 0x0f) << 4) | ((c1 & 0x3c) >> 2);
+
+ if (p[0] == '=')
+ break;
+ if (*p >= 128 || (c2 = index_64[*p++]) == -1)
+ return -1;
+ *t++ = ((c1 & 0x03) << 6) | c2;
+ }
+
+ return t - (unsigned char *) dst;
+}
+
+/* Return a pointer to a base 64 encoded string. The input data is
+ arbitrary binary data, the output is a \0 terminated string.
+ src and dst may not share the same buffer. */
+int
+b64_encode (char *dst, int dstlen, const void *src, int srclen)
+{
+ char *to = dst;
+ const unsigned char *from;
+ unsigned char c1, c2;
+ int dst_needed;
+
+ assert (dst != NULL && dstlen > 0 && srclen >= 0);
+
+ if (src == NULL)
+ return 0;
+
+ dst_needed = (srclen + 2) / 3;
+ dst_needed *= 4;
+ if (dstlen < dst_needed + 1)
+ return -1;
+
+ from = src;
+ while (srclen > 0)
+ {
+ c1 = *from++; srclen--;
+ *to++ = base64[c1 >> 2];
+ c1 = (c1 & 0x03) << 4;
+ if (srclen <= 0)
+ {
+ *to++ = base64[c1];
+ *to++ = '=';
+ *to++ = '=';
+ break;
+ }
+ c2 = *from++; srclen--;
+ c1 |= (c2 >> 4) & 0x0f;
+ *to++ = base64[c1];
+ c1 = (c2 & 0x0f) << 2;
+ if (srclen <= 0)
+ {
+ *to++ = base64[c1];
+ *to++ = '=';
+ break;
+ }
+ c2 = *from++; srclen--;
+ c1 |= (c2 >> 6) & 0x03;
+ *to++ = base64[c1];
+ *to++ = base64[c2 & 0x3f];
+ }
+ *to = '\0';
+ return to - dst;
+}
+
diff --git a/base64.h b/base64.h
new file mode 100644
index 0000000..37fe03a
--- /dev/null
+++ b/base64.h
@@ -0,0 +1,28 @@
+#ifndef _base64_h
+#define _base64_h
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+int b64_decode (void *dst, int dstlen, const char *src, int srclen);
+int b64_encode (char *dst, int dstlen, const void *src, int srclen);
+
+#endif
diff --git a/concatenate.c b/concatenate.c
new file mode 100644
index 0000000..9b0a33d
--- /dev/null
+++ b/concatenate.c
@@ -0,0 +1,193 @@
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* Stuff to maintain a dynamically sized buffer for a string. I seem
+ to keep writing code like this, so I've put it in a file by itself.
+ Maybe I should consolidate some other string buffering code with this.
+
+ NOTE: no \0 terminating strings
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include <missing.h> /* declarations for missing library functions */
+
+#include "concatenate.h"
+
+/* malloc/realloc the buffer to be the requested length.
+ Return 0 on failure, non-zero otherwise */
+static int
+cat_alloc (struct catbuf *catbuf, size_t length)
+{
+ char *nbuf;
+
+ assert (catbuf != NULL && length > 0);
+
+ if (catbuf->buffer == NULL)
+ catbuf->buffer = malloc (length);
+ else
+ {
+ nbuf = realloc (catbuf->buffer, length);
+ if (nbuf == NULL)
+ free (catbuf->buffer);
+ catbuf->buffer = nbuf;
+ }
+ catbuf->allocated = (catbuf->buffer == NULL) ? 0 : length;
+ if (catbuf->allocated < catbuf->string_length)
+ catbuf->string_length = catbuf->allocated;
+ return catbuf->buffer != NULL;
+}
+
+/* Reset the string to zero length without freeing the allocated memory */
+void
+cat_reset (struct catbuf *catbuf, size_t minimum_length)
+{
+ assert (catbuf != NULL);
+
+ catbuf->string_length = 0;
+ if (minimum_length > catbuf->allocated)
+ cat_alloc (catbuf, minimum_length);
+}
+
+/* Initialise a buffer */
+void
+cat_init (struct catbuf *catbuf, size_t minimum_length)
+{
+ assert (catbuf != NULL);
+
+ memset (catbuf, 0, sizeof (struct catbuf));
+ if (minimum_length > 0)
+ cat_alloc (catbuf, minimum_length);
+}
+
+/* Free memory allocated to the buffer */
+void
+cat_free (struct catbuf *catbuf)
+{
+ assert (catbuf != NULL);
+
+ if (catbuf->buffer != NULL)
+ free (catbuf->buffer);
+ memset (catbuf, 0, sizeof (struct catbuf));
+}
+
+/* Return a pointer to the buffer and put its length in *len. */
+char *
+cat_buffer (struct catbuf *catbuf, int *len)
+{
+ assert (catbuf != NULL);
+
+ if (len != NULL)
+ *len = catbuf->string_length;
+ return catbuf->buffer;
+}
+
+/* Shrink the allocated memory to the minimum needed. Return a
+ pointer to the buffer and put its length in *len. */
+char *
+cat_shrink (struct catbuf *catbuf, int *len)
+{
+ assert (catbuf != NULL);
+
+ if (catbuf->buffer != NULL)
+ cat_alloc (catbuf, catbuf->string_length);
+ if (len != NULL)
+ *len = catbuf->string_length;
+ return catbuf->buffer;
+}
+
+/* Concatenate a string to the buffer. N.B. the buffer is NOT terminated
+ by a \0. If len < 0 then string must be \0 terminated. */
+char *
+concatenate (struct catbuf *catbuf, const char *string, int len)
+{
+ size_t shortfall;
+
+ assert (catbuf != NULL && string != NULL);
+
+ if (len < 0)
+ len = strlen (string);
+ if (len > 0)
+ {
+ /* Ensure that the buffer is big enough to accept the string */
+ if (catbuf->buffer == NULL)
+ shortfall = 512;
+ else
+ shortfall = len - (catbuf->allocated - catbuf->string_length);
+ if (shortfall > 0)
+ {
+ if (shortfall % 128 != 0)
+ shortfall += 128 - shortfall % 128;
+ if (!cat_alloc (catbuf, catbuf->allocated + shortfall))
+ return NULL;
+ }
+
+ /* Copy the string */
+ memcpy (catbuf->buffer + catbuf->string_length, string, len);
+ catbuf->string_length += len;
+ }
+ return catbuf->buffer;
+}
+
+char *
+vconcatenate (struct catbuf *catbuf, ...)
+{
+ va_list alist;
+ const char *string;
+
+ assert (catbuf != NULL);
+
+ va_start (alist, catbuf);
+ while ((string = va_arg (alist, const char *)) != NULL)
+ concatenate (catbuf, string, -1);
+ va_end (alist);
+ return catbuf->buffer;
+}
+
+int
+cat_printf (struct catbuf *catbuf, const char *format, ...)
+{
+ va_list alist;
+ char buf[1024];
+ int len;
+
+ assert (catbuf != NULL && format != NULL);
+
+ va_start (alist, format);
+ len = vsnprintf (buf, sizeof buf, format, alist);
+ va_end (alist);
+ if (len <= 0)
+ return len;
+ if (len >= (int) sizeof buf)
+ len = sizeof buf;
+ concatenate (catbuf, buf, len);
+ return len;
+}
diff --git a/concatenate.h b/concatenate.h
new file mode 100644
index 0000000..fc0e54b
--- /dev/null
+++ b/concatenate.h
@@ -0,0 +1,42 @@
+#ifndef _concatenate_h
+#define _concatenate_h
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+struct catbuf
+ {
+ char *buffer;
+ size_t string_length;
+ size_t allocated;
+ };
+
+void cat_init (struct catbuf *catbuf, size_t minimum_length);
+void cat_reset (struct catbuf *catbuf, size_t minimum_length);
+void cat_free (struct catbuf *catbuf);
+char *cat_shrink (struct catbuf *catbuf, int *len);
+char *cat_buffer (struct catbuf *catbuf, int *len);
+char *concatenate (struct catbuf *catbuf, const char *string, int len);
+char *vconcatenate (struct catbuf *catbuf, ...);
+int cat_printf (struct catbuf *catbuf, const char *format, ...)
+ __attribute__ ((format (printf, 2, 3))) ;
+
+#endif
diff --git a/configure.in b/configure.in
new file mode 100644
index 0000000..997f575
--- /dev/null
+++ b/configure.in
@@ -0,0 +1,618 @@
+dnl Process this file with autoconf to produce a configure script.
+
+dnl #########################################################################
+dnl
+dnl This file is part of libESMTP, a library for submission of RFC 2822
+dnl formatted electronic mail messages using the SMTP protocol described
+dnl in RFC 2821.
+dnl
+dnl Copyright (C) 2001 Brian Stafford <brian@stafford.uklinux.net>
+dnl
+dnl This library is free software; you can redistribute it and/or
+dnl modify it under the terms of the GNU Lesser General Public
+dnl License as published by the Free Software Foundation; either
+dnl version 2.1 of the License, or (at your option) any later version.
+dnl
+dnl This library is distributed in the hope that it will be useful,
+dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+dnl Lesser General Public License for more details.
+dnl
+dnl You should have received a copy of the GNU Lesser General Public
+dnl License along with this library; if not, write to the Free Software
+dnl Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+dnl
+dnl #########################################################################
+
+AC_INIT(smtp-api.c)
+AM_CONFIG_HEADER(config.h)
+AM_INIT_AUTOMAKE([libesmtp],[1.0.4])
+AC_CANONICAL_HOST
+
+dnl #########################################################################
+dnl Library versioning
+dnl #########################################################################
+
+LIB_CURRENT=6
+LIB_REVISION=5
+LIB_AGE=1
+AC_SUBST(LIB_CURRENT)
+AC_SUBST(LIB_REVISION)
+AC_SUBST(LIB_AGE)
+
+LIBESMTP_VERSION="$LIB_CURRENT:$LIB_REVISION:$LIB_AGE"
+AC_SUBST(LIBESMTP_VERSION)
+subdirs=
+
+dnl #########################################################################
+dnl Checks for programs.
+dnl #########################################################################
+AC_PROG_CC
+AC_PROG_INSTALL
+AC_PROG_MAKE_SET
+AM_PROG_LIBTOOL
+
+dnl #########################################################################
+dnl Miscellaneous stuff
+dnl #########################################################################
+
+EXTRA_CFLAGS=""
+case $host_vendor-$host_os in
+sun*)
+ AC_DEFINE(__EXTENSIONS__,1,[Sun's netdb.h needs this for getaddrinfo])
+ ;;
+osf*)
+ AC_DEFINE(_OSF_SOURCE,1,[OSF needs this for getaddrinfo])
+ ;;
+*gnu*)
+ AC_DEFINE(_GNU_SOURCE,1,[GNU needs this for strcasecmp etc])
+ ;;
+esac
+
+dnl #########################################################################
+dnl turn warnings into errors to enforce clean code
+dnl #########################################################################
+
+AC_ARG_ENABLE([more-warnings],
+ ACX_HELP_STRING([--enable-more-warnings=no/yes/picky],[additional compiler warnings (default=yes)]),
+ ,
+ enable_more_warnings=yes)
+
+if test "$GCC" = "yes" -a "$enable_more_warnings" != "no"; then
+ EXTRA_CFLAGS="$EXTRA_CFLAGS \
+ -Wall -Wchar-subscripts -Wmissing-declarations -Wmissing-prototypes \
+ -Wstrict-prototypes -Wnested-externs -Wpointer-arith \
+ -Wbad-function-cast -Wcast-align"
+fi
+if test "$GCC" = "yes" -a "$enable_more_warnings" = "picky"; then
+ EXTRA_CFLAGS="$EXTRA_CFLAGS -fno-builtin -W -Werror -Wwrite-strings \
+ -Wcast-qual"
+fi
+
+AC_ARG_ENABLE([isoc],
+ ACX_HELP_STRING([--disable-isoc],[check ISO C compliance (default=c99)]),
+ ,
+ enable_isoc=c99)
+if test "$enable_isoc" != "no"; then
+ if test "$GCC" = "yes"; then
+ CFLAGS="-std=$enable_isoc -pedantic $CFLAGS"
+ fi
+ AC_DEFINE(_ISOC9X_SOURCE,1,[ISO compliant code])
+ AC_DEFINE(_ISOC99_SOURCE,1,[ISO compliant code])
+ AC_DEFINE(_POSIX_C_SOURCE,199506L,[POSIX compliant code])
+ AC_DEFINE(_XOPEN_SOURCE,500,[Single Unix conformance])
+fi
+
+dnl #########################################################################
+dnl Checks for libraries and options.
+dnl #########################################################################
+
+AC_ARG_ENABLE([all],ACX_HELP_STRING([--enable-all], [convenience: enable all experimental features]),
+ ,
+ enable_all=no)
+
+dnl #########################################################################
+dnl Check whether to use SUS functions or libltdl for loading plugins.
+dnl #########################################################################
+
+have_dl=no
+AC_SEARCH_LIBS(dlsym, dl svdl, [
+ AC_DEFINE([HAVE_DLSYM],1,[the dlsym() function is available])
+ have_dl=yes
+])
+if test x$have_dl = xno ; then
+dnl # WARNING: I don't know what libraries ltdl depends on. I just assume
+dnl # that if the punter asks for it, then it must be properly
+dnl # installed and the dynamic linker will sort the dependencies
+dnl # at link time.
+
+ AC_SEARCH_LIBS(lt_dlsym, ltdl,
+ have_dl=yes,
+ AC_MSG_ERROR([libltdl not found (available from http://www.gnu.org/software/libtool/)])
+ )
+fi
+
+dnl #########################################################################
+dnl Check if using Posix Threads
+dnl #########################################################################
+
+AC_ARG_ENABLE([pthreads],
+ ACX_HELP_STRING([--enable-pthreads], [build with support for Posix threads (default=auto)]),
+ ,
+ enable_pthreads=auto)
+
+if test x"$enable_pthreads" != xno ; then
+ ACX_PTHREAD(enable_pthreads=yes,
+ if test x"$enable_pthreads" = xyes ; then
+ AC_MSG_ERROR([Cannot find the pthread library.])
+ else
+ enable_pthreads=no
+ fi
+ )
+fi
+
+AC_MSG_CHECKING(whether to use Posix Threads)
+if test x"$enable_pthreads" != xno ; then
+ CFLAGS="$PTHREAD_CFLAGS $CFLAGS"
+ LDFLAGS="$PTHREAD_LDFLAGS $LDFLAGS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ CC="$PTHREAD_CC"
+ AC_DEFINE([USE_PTHREADS],1,[Build with support for Posix threading])
+ AC_MSG_RESULT([yes])
+else
+ AC_MSG_RESULT([no])
+fi
+
+dnl #########################################################################
+dnl Check if Posix getaddrinfo() is available. It is also possible to use
+dnl the version from the lwres library distributed with BIND.
+dnl #########################################################################
+AC_ARG_ENABLE([emulate-getaddrinfo],
+ ACX_HELP_STRING([--enable-emulate-getaddrinfo],
+ [enable getaddrinfo emulation (default=no)]),
+ ,
+ enable_emulate_getaddrinfo=no)
+AC_ARG_WITH(lwres,
+ ACX_HELP_STRING([--with-lwres=DIR],
+ [use lwres library for getaddrinfo (default=no)]),
+ ,
+ with_lwres=no)
+
+dnl ## enable force to test getaddrinfo.c
+if test x$enable_emulate_getaddrinfo = xforce ; then
+ enable_emulate_getaddrinfo=yes
+ have_getaddrinfo=no
+else
+
+have_getaddrinfo=no
+if test x$with_lwres != xno ; then
+ if test "$with_lwres" != yes ; then
+ CPPFLAGS="-I${with_lwres}/include $CPPFLAGS"
+ LDFLAGS="-L${with_lwres}/lib $LDFLAGS"
+ fi
+ AC_CHECK_HEADERS(lwres/netdb.h, ,
+ [AC_MSG_ERROR([cannot find <lwres/netdb.h>])])
+ AC_CHECK_LIB(lwres, lwres_getaddrinfo, ,
+ [AC_MSG_ERROR([cannot find the lwres library])],
+ -lnsl -lpthread)
+ have_getaddrinfo=yes
+fi
+
+if test x$have_getaddrinfo != xyes ; then
+ AC_SEARCH_LIBS(getaddrinfo, socket resolv bind nsl c_r cr, have_getaddrinfo=yes)
+fi
+
+dnl # Special nonsense for systems that actually have getaddrinfo but
+dnl # redefine the name to something else, e.g. OSF
+if test x$have_getaddrinfo != xyes ; then
+ AC_MSG_CHECKING(if getaddrinfo is redefined in netdb.h)
+ AC_TRY_LINK([
+# include <netdb.h>
+ ], [
+ struct addrinfo hints, *res;
+ int err;
+
+ err = getaddrinfo ("host", "service", &hints, &res);
+ ], [
+ have_getaddrinfo=yes
+ AC_MSG_RESULT(yes)
+ ], [AC_MSG_RESULT(no)])
+fi
+
+fi
+
+if test x$have_getaddrinfo != xno ; then
+ if test x$enable_emulate_getaddrinfo != xno ; then
+ AC_MSG_ERROR([getaddrinfo found but emulate-getaddrinfo was enabled])
+ fi
+ AC_DEFINE(HAVE_GETADDRINFO, 1,
+ [Does system provide RFC 2553/Posix getaddrinfo?])
+else
+ if test x$enable_emulate_getaddrinfo != xyes ; then
+ AC_MSG_ERROR([getaddrinfo not found: try --with-lwres or --enable-emulate-getaddrinfo])
+ fi
+ AC_LIBOBJ(getaddrinfo)
+fi
+
+if test x"$enable_emulate_getaddrinfo" != xno ; then
+ have_resolver=no
+
+ dnl Try for getipnodebyname
+ AC_SEARCH_LIBS(getipnodebyname, resolv bind nsl c_r cr, have_resolver=yes)
+ if test x"$have_resolver" != xno ; then
+ AC_DEFINE(HAVE_GETIPNODEBYNAME, 1,
+ [Set when getipnodebyname is available])
+ fi
+
+ dnl Try for gethostbyname_r
+ if test x"$have_resolver" = xno ; then
+ AC_SEARCH_LIBS(gethostbyname_r, resolv bind nsl c_r cr,
+ [have_resolver=yes
+ ACX_WHICH_GETHOSTBYNAME_R])
+ fi
+
+ dnl Try for gethostbyname
+ if test x"$have_resolver" = xno ; then
+ if test x"$enable_pthreads" != xno ; then
+ AC_MSG_WARN([using threads but cannot find gethostbyname_r or getipnodebyname])
+ fi
+ AC_SEARCH_LIBS(gethostbyname, resolv bind nsl, ,
+ [AC_MSG_ERROR([cannot find gethostbyname])])
+ fi
+ AC_LIBOBJ(gethostbyname)
+
+ AC_CACHE_CHECK([for IPv6 support], acx_cv_sys_use_ipv6, [
+ AC_TRY_COMPILE([
+# include <netinet/in.h>
+ ], [
+ struct sockaddr_in6 sin6;
+ void *p;
+
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = 587;
+ p = &sin6.sin6_addr;
+ ], [acx_cv_sys_use_ipv6=yes], [acx_cv_sys_use_ipv6=no])
+ ])
+ if test x"$acx_cv_sys_use_ipv6" != xno ; then
+ AC_DEFINE(USE_IPV6,1,[Enable IPv6 support])
+ fi
+fi
+
+dnl #########################################################################
+dnl Check if using OpenSSL
+dnl #########################################################################
+AC_ARG_WITH(openssl,
+ ACX_HELP_STRING([--with-openssl=DIR],
+ [build features depending on OpenSSL (default=auto)]),
+ ,
+ with_openssl=auto)
+if test x$with_openssl != xno ; then
+ if test "$with_openssl" != yes -a "$with_openssl" != auto ; then
+ CPPFLAGS="-I${with_openssl}/include $CPPFLAGS"
+ LDFLAGS="-L${with_openssl}/lib $LDFLAGS"
+ with_openssl=yes
+ fi
+ dnl *** with_openssl is either "yes" or "auto"
+ AC_CHECK_HEADER(openssl/ssl.h,,
+ if test x"$with_openssl" = xyes ; then
+ AC_MSG_ERROR([cannot find the ssl headers])
+ else
+ with_openssl=no
+ fi
+ )
+fi
+if test x$with_openssl != xno ; then
+ AC_CHECK_LIB(ssl, SSL_library_init, [
+ with_openssl=yes
+ LIBS="-lssl -lcrypto $LIBS"
+ ], [
+ if test x"$with_openssl" = xyes ; then
+ AC_MSG_ERROR([cannot find the ssl library])
+ else
+ with_openssl=no
+ fi],
+ -lcrypto
+ )
+fi
+
+dnl #########################################################################
+dnl Set OpenSSL dependencies for RPM - this sucks, there must be a better way.
+dnl #########################################################################
+
+RPM_REQUIRES=""
+RPM_BUILDREQUIRES=""
+RPM_OPENSSL=""
+RPM_OPENSSLDEVEL=""
+if test x$with_openssl != xno ; then
+ RPM_REQUIRES="Requires:"
+ RPM_BUILDREQUIRES="BuildRequires:"
+ RPM_OPENSSL="openssl >= %{openssl}"
+ RPM_OPENSSLDEVEL="openssl-devel >= %{openssl}"
+fi
+AC_SUBST(RPM_REQUIRES)
+AC_SUBST(RPM_BUILDREQUIRES)
+AC_SUBST(RPM_OPENSSL)
+AC_SUBST(RPM_OPENSSLDEVEL)
+
+dnl #########################################################################
+dnl Check if using SMTP AUTH using SASL
+dnl #########################################################################
+
+dnl Force use of SMTP AUTH for now. Eventually it is hoped that
+dnl the SASL API will move into a library of its own.
+
+AC_DEFINE(USE_SASL,1,[Build with support for SMTP AUTH using SASL])
+SASL_PLUGINS="login plain crammd5"
+DIST_PLUGINS="login plain crammd5"
+
+dnl Set up the authentication plugin directory
+
+AC_ARG_WITH(auth-plugin-dir,
+ ACX_HELP_STRING([--with-auth-plugin-dir=DIR],
+ [directory for SASL plugins (default=LIBDIR/esmtp-plugins)]),
+ plugindir=$withval)
+if test x$plugindir = x ; then
+ plugindir=$libdir/esmtp-plugins
+fi
+AC_SUBST(plugindir)
+ACX_DEFINE_DIR([AUTHPLUGINDIR], $plugindir, [location of authentication plugins.])
+
+have_libcrypto=no
+
+dnl Check for md5 functions in OpenSSL. If these are present, use them
+dnl in preference to the supplied md5.[ch] when building the CRAM-MD5
+dnl SASL plugin. The assumption is that the OpenSSL functions are optimised
+dnl and actively maintained.
+
+use_local_md5_c=yes
+CRAMMD5_LIBOBJS=""
+if test x$with_openssl != xno ; then
+ AC_CHECK_HEADER(openssl/md5.h, [
+ AC_CHECK_LIB(crypto, MD5_Init, [
+ have_libcrypto=yes
+ use_local_md5_c=no
+ CRAMMD5_LIBS="-lcrypto"
+ AC_SUBST(CRAMMD5_LIBS)
+ ])
+ ])
+fi
+if test x$use_local_md5_c != xno ; then
+ CRAMMD5_LIBOBJS="$CRAMMD5_LIBOBJS md5.o"
+fi
+CRAMMD5_LTLIBOBJS=`echo "$CRAMMD5_LIBOBJS" | sed ['s/\.[^.]* /.lo /g;s/\.[^.]*$/.lo/']`
+AC_SUBST(CRAMMD5_LTLIBOBJS)
+
+dnl Check if enabling experimental NTLM authentication. This requires that
+dnl OpenSSL is available and that it is explicitly requested.
+
+AC_ARG_ENABLE([ntlm],
+ ACX_HELP_STRING([--enable-ntlm],[experimental support for NTLM authentication (default=no)]),
+ ,
+ enable_ntlm=$enable_all)
+DIST_PLUGINS="$DIST_PLUGINS ntlm"
+
+AC_MSG_CHECKING(whether to enable NTLM authentication)
+if test x$with_openssl != xno -a x"$enable_ntlm" = xyes ; then
+ AC_MSG_RESULT([yes])
+ AC_CHECK_HEADER(openssl/md4.h, [
+ AC_CHECK_LIB(crypto, MD4_Init, [
+ have_libcrypto=yes
+ SASL_PLUGINS="$SASL_PLUGINS ntlm"
+ NTLM_LIBS="-lcrypto"
+ AC_SUBST(NTLM_LIBS)
+ ])
+ ])
+else
+ AC_MSG_RESULT([no])
+fi
+
+if test x$have_libcrypto != xno ; then
+ AC_DEFINE(HAVE_LIBCRYPTO,1,[Set when -lcrypto from OpenSSL is available])
+fi
+
+dnl #########################################################################
+dnl Check if enabling STARTTLS
+dnl #########################################################################
+
+AC_MSG_CHECKING(whether to enable STARTTLS)
+if test x"$with_openssl" != xno ; then
+ AC_MSG_RESULT([yes])
+ AC_DEFINE(USE_TLS,1,[Build with support for SMTP STARTTLS extension])
+else
+ AC_MSG_RESULT([no])
+fi
+
+dnl #########################################################################
+dnl Enable CHUNKING
+dnl #########################################################################
+
+AC_ARG_ENABLE([chunking],
+ ACX_HELP_STRING([--disable-chunking],[support for CHUNKING (default=yes)]),
+ ,
+ enable_chunking=yes)
+
+AC_MSG_CHECKING(whether to enable CHUNKING)
+if test x"$enable_chunking" = xyes ; then
+ AC_MSG_RESULT([yes])
+ AC_DEFINE(USE_CHUNKING,1,[Enable experimental support for SMTP CHUNKING extension])
+else
+ AC_MSG_RESULT([no])
+fi
+
+dnl #########################################################################
+dnl Enable ETRN (experimental)
+dnl #########################################################################
+
+AC_ARG_ENABLE([etrn],
+ ACX_HELP_STRING([--enable-etrn],[experimental support for ETRN (default=no)]),
+ ,
+ enable_etrn=$enable_all)
+
+AC_MSG_CHECKING(whether to enable ETRN)
+if test x"$enable_etrn" = xyes ; then
+ AC_MSG_RESULT([yes])
+ AC_DEFINE(USE_ETRN,1,[Enable experimental support for SMTP ETRN extension])
+else
+ AC_MSG_RESULT([no])
+fi
+
+dnl #########################################################################
+dnl Enable XUSR
+dnl #########################################################################
+
+AC_ARG_ENABLE([xusr],
+ ACX_HELP_STRING([--disable-xusr],
+ [support for sendmail XUSR extension (default=yes)]),
+ ,
+ enable_xusr=yes)
+
+AC_MSG_CHECKING(whether to enable sendmail XUSR)
+if test x"$enable_xusr" = xyes ; then
+ AC_MSG_RESULT([yes])
+ AC_DEFINE(USE_XUSR,1,[Enable support for sendmail XUSR extension])
+else
+ AC_MSG_RESULT([no])
+fi
+
+dnl #########################################################################
+dnl Enable non-standard AUTH= syntax in EHLO response
+dnl #########################################################################
+
+AC_ARG_ENABLE([nsauth],
+ ACX_HELP_STRING([--disable-nsauth],
+ [support non-standard EHLO AUTH= response (default=yes)]),
+ ,
+ enable_nsauth=yes)
+
+AC_MSG_CHECKING([whether to enable non-standard EHLO AUTH= response])
+if test x"$enable_nsauth" = xyes ; then
+ AC_MSG_RESULT([yes])
+ AC_DEFINE(AUTH_ID_HACK,1,[support non-standard EHLO AUTH= response])
+else
+ AC_MSG_RESULT([no])
+fi
+
+dnl #########################################################################
+dnl Enable debugging code, e.g. assert()
+dnl #########################################################################
+
+AC_ARG_ENABLE([debug],
+ ACX_HELP_STRING([--enable-debug],
+ [enable use of debugging code (default=no)]),
+ ,
+ enable_debug=no)
+
+if test x"$enable_debug" != xno ; then
+ AC_DEFINE(DEBUG,1, [Enable additional debugging code])
+else
+ AC_DEFINE(NDEBUG,1, [Disable assertions])
+fi
+
+dnl #########################################################################
+dnl Checks for header files.
+dnl #########################################################################
+
+AC_HEADER_STDC
+AC_CHECK_HEADERS(sys/ioctl.h unistd.h)
+
+dnl #########################################################################
+dnl Checks for typedefs, structures, and compiler characteristics.
+dnl #########################################################################
+
+AC_C_CONST
+AC_C_INLINE
+AC_C_BIGENDIAN
+AC_STRUCT_TM
+AC_HEADER_TIME
+AC_STRUCT_TIMEZONE
+
+dnl #########################################################################
+dnl Check for types
+dnl #########################################################################
+AC_CHECK_SIZEOF(unsigned short)
+AC_CHECK_SIZEOF(unsigned int)
+AC_CHECK_SIZEOF(unsigned long)
+
+dnl #########################################################################
+dnl Checks for library functions.
+dnl #########################################################################
+
+AC_REPLACE_FUNCS(strdup strcasecmp strncasecmp memrchr)
+AC_CHECK_FUNCS(strtol uname gethostname gettimeofday)
+AC_SEARCH_LIBS(socket, socket)
+
+dnl Conditional check for functions needed in threaded code
+
+if test x"$enable_pthreads" != xno ; then
+ AC_SEARCH_LIBS(localtime_r, c_r cr,
+ AC_DEFINE(HAVE_LOCALTIME_R,1,[Set when localtime_r is available]),
+ AC_MSG_ERROR([Cannot find localtime_r.]))
+ AC_CHECK_FUNCS(strerror_r,[
+ AC_CACHE_CHECK([for working strerror_r],
+ acx_cv_sys_working_strerror_r, [
+ AC_TRY_COMPILE([
+# define _SVID_SOURCE 1
+# include <string.h>
+ ], [
+ char *s;
+
+ s = strerror_r (0, (char *) 0, 0);
+ ],
+ [acx_cv_sys_working_strerror_r=yes],
+ [acx_cv_working_strerror_r=no])
+ ])
+ if test x$acx_cv_sys_working_strerror_r = xyes ; then
+ AC_DEFINE(HAVE_WORKING_STRERROR_R,1,[strerror_r works!])
+ fi
+ ])
+else
+ AC_CHECK_FUNCS(localtime strerror)
+fi
+
+dnl #########################################################################
+dnl Check that snprintf works correctly.
+dnl #########################################################################
+ACX_SNPRINTF(,[AC_LIBOBJ(snprintf)])
+
+dnl #########################################################################
+dnl Make substitutions
+dnl #########################################################################
+
+AC_SUBST(SASL_PLUGINS)
+AC_SUBST(DIST_PLUGINS)
+AC_SUBST(LIBTOOL_DEPS)
+AC_SUBST(LIBS)
+AC_SUBST(CFLAGS)
+AC_SUBST(EXTRA_CFLAGS)
+AC_SUBST(CC)
+AC_SUBST(RANLIB)
+AC_SUBST(subdirs)
+
+dnl ## LTLIBOBJS=`echo "$LIBOBJS" | sed ['s/\.[^.]* /.lo /g;s/\.[^.]*$/.lo/']`
+AC_SUBST(LTLIBOBJS)
+LTALLOCA=`echo "$ALLOCA" | sed ['s/\.[^.]* /.lo /g;s/\.[^.]*$/.lo/']`
+AC_SUBST(LTALLOCA)
+
+AC_OUTPUT([libesmtp-config libesmtp.spec
+ Makefile login/Makefile plain/Makefile crammd5/Makefile ntlm/Makefile])
+
+dnl #########################################################################
+dnl Feature synopsis
+dnl #########################################################################
+
+echo
+echo '*************************'
+echo '*** libESMTP features ***'
+echo '*************************'
+d=`eval echo $plugindir`
+ACX_FEATURE([with],[auth-plugin-dir],[`eval echo $d`])
+ACX_FEATURE([with],[lwres])
+ACX_FEATURE([with],[openssl])
+ACX_FEATURE([enable],[pthreads])
+ACX_FEATURE([enable],[etrn])
+ACX_FEATURE([enable],[ntlm])
+ACX_FEATURE([enable],[chunking])
+ACX_FEATURE([enable],[xusr])
+ACX_FEATURE([enable],[nsauth])
+ACX_FEATURE([enable],[debug])
+
diff --git a/crammd5/Makefile.am b/crammd5/Makefile.am
new file mode 100644
index 0000000..d209ae7
--- /dev/null
+++ b/crammd5/Makefile.am
@@ -0,0 +1,14 @@
+## Process this file with automake to produce Makefile.in
+
+libdir = @plugindir@
+
+INCLUDES = -I@srcdir@
+AM_CFLAGS = @CFLAGS@ @EXTRA_CFLAGS@
+
+lib_LTLIBRARIES = sasl-cram-md5.la
+
+sasl_cram_md5_la_SOURCES = client-crammd5.c hmacmd5.c hmacmd5.h
+sasl_cram_md5_la_LDFLAGS = -module -avoid-version @CRAMMD5_LIBS@
+sasl_cram_md5_la_DEPENDENCIES = @CRAMMD5_LTLIBOBJS@
+sasl_cram_md5_la_LIBADD = @CRAMMD5_LTLIBOBJS@
+EXTRA_DIST = md5.h md5.c
diff --git a/crammd5/Makefile.in b/crammd5/Makefile.in
new file mode 100644
index 0000000..73d2970
--- /dev/null
+++ b/crammd5/Makefile.in
@@ -0,0 +1,479 @@
+# Makefile.in generated by automake 1.9.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+SOURCES = $(sasl_cram_md5_la_SOURCES)
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = crammd5
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
+am__installdirs = "$(DESTDIR)$(libdir)"
+libLTLIBRARIES_INSTALL = $(INSTALL)
+LTLIBRARIES = $(lib_LTLIBRARIES)
+am_sasl_cram_md5_la_OBJECTS = client-crammd5.lo hmacmd5.lo
+sasl_cram_md5_la_OBJECTS = $(am_sasl_cram_md5_la_OBJECTS)
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(sasl_cram_md5_la_SOURCES)
+DIST_SOURCES = $(sasl_cram_md5_la_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CRAMMD5_LIBS = @CRAMMD5_LIBS@
+CRAMMD5_LTLIBOBJS = @CRAMMD5_LTLIBOBJS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DIST_PLUGINS = @DIST_PLUGINS@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+EXTRA_CFLAGS = @EXTRA_CFLAGS@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBESMTP_VERSION = @LIBESMTP_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBTOOL_DEPS = @LIBTOOL_DEPS@
+LIB_AGE = @LIB_AGE@
+LIB_CURRENT = @LIB_CURRENT@
+LIB_REVISION = @LIB_REVISION@
+LN_S = @LN_S@
+LTALLOCA = @LTALLOCA@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+NTLM_LIBS = @NTLM_LIBS@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LDFLAGS = @PTHREAD_LDFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+RANLIB = @RANLIB@
+RPM_BUILDREQUIRES = @RPM_BUILDREQUIRES@
+RPM_OPENSSL = @RPM_OPENSSL@
+RPM_OPENSSLDEVEL = @RPM_OPENSSLDEVEL@
+RPM_REQUIRES = @RPM_REQUIRES@
+SASL_PLUGINS = @SASL_PLUGINS@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+ac_ct_RANLIB = @ac_ct_RANLIB@
+ac_ct_STRIP = @ac_ct_STRIP@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
+am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @plugindir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+plugindir = @plugindir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+subdirs = @subdirs@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+INCLUDES = -I@srcdir@
+AM_CFLAGS = @CFLAGS@ @EXTRA_CFLAGS@
+lib_LTLIBRARIES = sasl-cram-md5.la
+sasl_cram_md5_la_SOURCES = client-crammd5.c hmacmd5.c hmacmd5.h
+sasl_cram_md5_la_LDFLAGS = -module -avoid-version @CRAMMD5_LIBS@
+sasl_cram_md5_la_DEPENDENCIES = @CRAMMD5_LTLIBOBJS@
+sasl_cram_md5_la_LIBADD = @CRAMMD5_LTLIBOBJS@
+EXTRA_DIST = md5.h md5.c
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu crammd5/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnu crammd5/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(libdir)" || $(mkdir_p) "$(DESTDIR)$(libdir)"
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ if test -f $$p; then \
+ f=$(am__strip_dir) \
+ echo " $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(libdir)/$$f"; \
+ else :; fi; \
+ done
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @set -x; list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ p=$(am__strip_dir) \
+ echo " $(LIBTOOL) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$p'"; \
+ $(LIBTOOL) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$p"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+sasl-cram-md5.la: $(sasl_cram_md5_la_OBJECTS) $(sasl_cram_md5_la_DEPENDENCIES)
+ $(LINK) -rpath $(libdir) $(sasl_cram_md5_la_LDFLAGS) $(sasl_cram_md5_la_OBJECTS) $(sasl_cram_md5_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client-crammd5.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hmacmd5.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ if $(LTCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+distclean-libtool:
+ -rm -f libtool
+uninstall-info-am:
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+ list='$(DISTFILES)'; for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+ esac; \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkdir_p) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)"; do \
+ test -z "$$dir" || $(mkdir_p) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-info-am uninstall-libLTLIBRARIES
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libLTLIBRARIES clean-libtool ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-exec \
+ install-exec-am install-info install-info-am \
+ install-libLTLIBRARIES install-man install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags uninstall uninstall-am uninstall-info-am \
+ uninstall-libLTLIBRARIES
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/crammd5/client-crammd5.c b/crammd5/client-crammd5.c
new file mode 100644
index 0000000..02a5163
--- /dev/null
+++ b/crammd5/client-crammd5.c
@@ -0,0 +1,142 @@
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include "hmacmd5.h"
+#include "auth-client.h"
+#include "auth-plugin.h"
+
+#define NELT(x) (sizeof x / sizeof x[0])
+
+static int crammd5_init (void *pctx);
+static void crammd5_destroy (void *ctx);
+static const char *crammd5_response (void *ctx,
+ const char *challenge, int *len,
+ auth_interact_t interact, void *arg);
+
+const struct auth_client_plugin sasl_client =
+ {
+ /* Plugin information */
+ "CRAM-MD5",
+ "Challenge-Response Authentication Mechanism (RFC 2195)",
+ /* Plugin instance */
+ crammd5_init,
+ crammd5_destroy,
+ /* Authentication */
+ crammd5_response,
+ 0,
+ /* Security Layer */
+ 0,
+ NULL,
+ NULL,
+ };
+
+static const struct auth_client_request client_request[] =
+ {
+ { "user", AUTH_CLEARTEXT | AUTH_USER, "User Name", 0, },
+ { "passphrase", AUTH_PASS, "Pass Phrase", 0, },
+ };
+
+struct crammd5_context
+ {
+ int state;
+ char *response;
+ int response_len;
+ };
+
+static int
+crammd5_init (void *pctx)
+{
+ struct crammd5_context *context;
+
+ context = malloc (sizeof (struct crammd5_context));
+ memset (context, 0, sizeof (struct crammd5_context));
+
+ *(void **) pctx = context;
+ return 1;
+}
+
+static void
+crammd5_destroy (void *ctx)
+{
+ struct crammd5_context *context = ctx;
+
+ if (context->response != NULL)
+ {
+ memset (context->response, 0, context->response_len);
+ free (context->response);
+ }
+ free (context);
+}
+
+static const char *
+crammd5_response (void *ctx, const char *challenge, int *len,
+ auth_interact_t interact, void *arg)
+{
+ struct crammd5_context *context = ctx;
+ char *result[NELT (client_request)];
+ unsigned char digest[16];
+ char *p, *response;
+ int response_len;
+ size_t i;
+ static const char hex[] = "0123456789abcdef";
+
+ switch (context->state)
+ {
+ case 0: /* No initial response */
+ context->state = 1;
+ *len = 0;
+ return NULL;
+
+ case 1: /* Digest the challenge and compute a response. */
+ if (!(*interact) (client_request, result, NELT (client_request), arg))
+ break;
+ hmac_md5 (challenge, *len, result[1], strlen (result[1]), digest);
+ response_len = strlen (result[0]) + 1 + 2 * sizeof digest;
+ response = malloc (response_len);
+ strcpy (response, result[0]);
+ strcat (response, " ");
+ p = strchr (response, '\0');
+ for (i = 0; i < sizeof digest; i++)
+ {
+ *p++ = hex[(digest[i] >> 4) & 0x0F];
+ *p++ = hex[digest[i] & 0x0F];
+ }
+ /* Note no \0 termination */
+ context->state = -1;
+ context->response = response;
+ context->response_len = response_len;
+ *len = response_len;
+ return response;
+ }
+ *len = 0;
+ return NULL;
+}
+
diff --git a/crammd5/hmacmd5.c b/crammd5/hmacmd5.c
new file mode 100644
index 0000000..80e2b33
--- /dev/null
+++ b/crammd5/hmacmd5.c
@@ -0,0 +1,120 @@
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <sys/types.h>
+#include "hmacmd5.h"
+
+#define PAD_SIZE 64
+
+/*
+ * the HMAC_MD5 transform looks like:
+ *
+ * MD5(K XOR opad, MD5(K XOR ipad, challenge))
+ *
+ * where K is an n byte secret
+ * ipad is the byte 0x36 repeated 64 times
+ * opad is the byte 0x5c repeated 64 times
+ * and challenge is the data being protected
+ */
+
+/* Precompute HMAC-MD5 contexts from a secret
+ */
+void
+hmac_md5_pre (const void *secret, size_t secret_len,
+ MD5_CTX *inner, MD5_CTX *outer)
+{
+ unsigned char ipad[PAD_SIZE];
+ unsigned char opad[PAD_SIZE];
+ unsigned char tk[16];
+ int i;
+
+ /* If secret is longer than 64 bytes reset it to secret = MD5 (secret)
+ */
+ if (secret_len > PAD_SIZE)
+ {
+ MD5_CTX tctx;
+
+ MD5_Init (&tctx);
+ MD5_Update (&tctx, secret, secret_len);
+ MD5_Final (tk, &tctx);
+ secret = tk;
+ secret_len = sizeof tk;
+ }
+
+ /* start out by storing secret in pads */
+ memcpy (ipad, secret, secret_len);
+ if (secret_len < sizeof ipad)
+ memset (ipad + secret_len, 0, sizeof ipad - secret_len);
+ memcpy (opad, secret, secret_len);
+ if (secret_len < sizeof opad)
+ memset (opad + secret_len, 0, sizeof opad - secret_len);
+
+ /* XOR secret with ipad and opad values */
+ for (i = 0; i < PAD_SIZE; i++)
+ {
+ ipad[i] ^= 0x36;
+ opad[i] ^= 0x5c;
+ }
+
+ /* perform inner MD5 */
+ MD5_Init (inner);
+ MD5_Update (inner, ipad, sizeof ipad);
+
+ /* perform outer MD5 */
+ MD5_Init (outer);
+ MD5_Update (outer, opad, sizeof opad);
+}
+
+/* Finalise HMAC-MD5 contexts from a challenge
+ */
+void
+hmac_md5_post (const void *challenge, size_t challenge_len,
+ MD5_CTX *inner, MD5_CTX *outer, unsigned char digest[16])
+{
+ unsigned char id[16];
+
+ /* perform inner MD5 */
+ MD5_Update (inner, challenge, challenge_len);
+ MD5_Final (id, inner);
+
+ /* perform outer MD5 */
+ MD5_Update (outer, id, sizeof id);
+ MD5_Final (digest, outer);
+}
+
+/* Digest a challenge and a secret.
+ */
+void
+hmac_md5 (const void *challenge, size_t challenge_len,
+ const void *secret, size_t secret_len,
+ unsigned char digest[16])
+{
+ MD5_CTX inner, outer;
+
+ hmac_md5_pre (secret, secret_len, &inner, &outer);
+ hmac_md5_post (challenge, challenge_len, &inner, &outer, digest);
+}
+
diff --git a/crammd5/hmacmd5.h b/crammd5/hmacmd5.h
new file mode 100644
index 0000000..2a46175
--- /dev/null
+++ b/crammd5/hmacmd5.h
@@ -0,0 +1,51 @@
+#ifndef _hmac_md5_h
+#define _hmac_md5_h
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#if HAVE_LIBCRYPTO
+
+#include <openssl/md5.h>
+
+#else
+
+#include "md5.h"
+
+#define MD5_CTX MD5Context
+#define MD5_Init(c) md5_init((c))
+#define MD5_Update(c,d,l) md5_update((c),(d),(l))
+#define MD5_Final(md,c) md5_final((c),(md))
+
+#endif
+
+/* Precompute HMAC-MD5 contexts from a secret. */
+void hmac_md5_pre (const void *secret, size_t secret_len,
+ MD5_CTX *inner, MD5_CTX *outer);
+/* Finalise HMAC-MD5 contexts from a challenge. */
+void hmac_md5_post (const void *challenge, size_t challenge_len,
+ MD5_CTX *inner, MD5_CTX *outer, unsigned char digest[16]);
+/* Digest a challenge and a secret. */
+void hmac_md5 (const void *challenge, size_t challenge_len,
+ const void *secret, size_t secret_len,
+ unsigned char digest[16]);
+
+#endif
diff --git a/crammd5/md5.c b/crammd5/md5.c
new file mode 100644
index 0000000..858a630
--- /dev/null
+++ b/crammd5/md5.c
@@ -0,0 +1,297 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to md5_init, call md5_update as
+ * needed on buffers full of bytes, and then call md5_Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+/* parts of this file are:
+ * Written March 1993 by Branko Lankester
+ * Modified June 1993 by Colin Plumb for altered md5.c.
+ * Modified October 1995 by Erik Troan for RPM
+ */
+
+/* Modified 2001 by Brian Stafford to use ISO C typedefs for various
+ * quantities. Also eliminated convenience functions not needed for
+ * libESMTP.
+ */
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include "md5.h"
+
+static void md5_transform (unsigned32_t buf[4], const unsigned32_t in[16]);
+
+static unsigned32_t _ie = 0x44332211;
+static union _endian { unsigned32_t i; unsigned char b[4]; } *_endian = (union _endian *)&_ie;
+#define IS_BIG_ENDIAN() (_endian->b[0] == '\x44')
+#define IS_LITTLE_ENDIAN() (_endian->b[0] == '\x11')
+
+
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void
+_byte_reverse (unsigned char *buf, unsigned32_t longs)
+{
+ unsigned32_t t;
+ do {
+ t = (unsigned32_t) ((unsigned32_t) buf[3] << 8 | buf[2]) << 16 |
+ ((unsigned32_t) buf[1] << 8 | buf[0]);
+ *(unsigned32_t *) buf = t;
+ buf += 4;
+ } while (--longs);
+}
+
+
+/**
+ * md5_init: Initialise an md5 context object
+ * @ctx: md5 context
+ *
+ * Initialise an md5 buffer.
+ *
+ **/
+void
+md5_init (MD5Context *ctx)
+{
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+
+ if (IS_BIG_ENDIAN())
+ ctx->doByteReverse = 1;
+ else
+ ctx->doByteReverse = 0;
+}
+
+
+/**
+ * md5_update: add a buffer to md5 hash computation
+ * @ctx: conetxt object used for md5 computaion
+ * @buf: buffer to add
+ * @len: buffer length
+ *
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes. Use this to progressively construct an md5 hash.
+ **/
+void
+md5_update (MD5Context *ctx, const void *buf, size_t len)
+{
+ unsigned32_t t;
+ const unsigned char *ucbuf = buf;
+
+ /* Update bitcount */
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((unsigned32_t) len << 3)) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+ if (t) {
+ unsigned char *p = (unsigned char *) ctx->in + t;
+
+ t = 64 - t;
+ if (len < t) {
+ memcpy (p, ucbuf, len);
+ return;
+ }
+ memcpy (p, ucbuf, t);
+ if (ctx->doByteReverse)
+ _byte_reverse (ctx->in, 16);
+ md5_transform (ctx->buf, (unsigned32_t *) ctx->in);
+ ucbuf += t;
+ len -= t;
+ }
+
+ /* Process data in 64-byte chunks */
+ while (len >= 64) {
+ memcpy (ctx->in, ucbuf, 64);
+ if (ctx->doByteReverse)
+ _byte_reverse (ctx->in, 16);
+ md5_transform (ctx->buf, (unsigned32_t *) ctx->in);
+ ucbuf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+ memcpy (ctx->in, ucbuf, len);
+}
+
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+/**
+ * md5_final: copy the final md5 hash to a bufer
+ * @digest: 16 bytes buffer
+ * @ctx: context containing the calculated md5
+ *
+ * copy the final md5 hash to a bufer
+ **/
+void
+md5_final (MD5Context *ctx, unsigned char digest[16])
+{
+ unsigned32_t count;
+ unsigned char *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset (p, 0, count);
+ if (ctx->doByteReverse)
+ _byte_reverse (ctx->in, 16);
+ md5_transform (ctx->buf, (unsigned32_t *) ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ memset (ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset (p, 0, count - 8);
+ }
+ if (ctx->doByteReverse)
+ _byte_reverse (ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ((unsigned32_t *) ctx->in)[14] = ctx->bits[0];
+ ((unsigned32_t *) ctx->in)[15] = ctx->bits[1];
+
+ md5_transform (ctx->buf, (unsigned32_t *) ctx->in);
+ if (ctx->doByteReverse)
+ _byte_reverse ((unsigned char *) ctx->buf, 4);
+ memcpy (digest, ctx->buf, 16);
+}
+
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. md5_Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void
+md5_transform (unsigned32_t buf[4], const unsigned32_t in[16])
+{
+ register unsigned32_t a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP (F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP (F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP (F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP (F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP (F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP (F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP (F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP (F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP (F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP (F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP (F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP (F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP (F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP (F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP (F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP (F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP (F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP (F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP (F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP (F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP (F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP (F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP (F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP (F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP (F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP (F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP (F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP (F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP (F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP (F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP (F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP (F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP (F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP (F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP (F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP (F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP (F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP (F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP (F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP (F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP (F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP (F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP (F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP (F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP (F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP (F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP (F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP (F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP (F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP (F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP (F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP (F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP (F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP (F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP (F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP (F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP (F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP (F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP (F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP (F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP (F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP (F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP (F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP (F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
diff --git a/crammd5/md5.h b/crammd5/md5.h
new file mode 100644
index 0000000..1274534
--- /dev/null
+++ b/crammd5/md5.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+/* parts of this file are :
+ * Written March 1993 by Branko Lankester
+ * Modified June 1993 by Colin Plumb for altered md5.c.
+ * Modified October 1995 by Erik Troan for RPM
+ */
+#ifndef MD5_UTILS_H
+#define MD5_UTILS_H
+
+#if SIZEOF_UNSIGNED_INT == 32 / 8
+typedef unsigned int unsigned32_t;
+#elif SIZEOF_UNSIGNED_LONG == 32 / 8
+typedef unsigned long unsigned32_t;
+#else
+#include <sys/types.h>
+typedef uint32 unsigned32_t;
+#endif
+
+#include <sys/types.h>
+
+typedef struct {
+ unsigned32_t buf[4];
+ unsigned32_t bits[2];
+ unsigned char in[64];
+ int doByteReverse;
+} MD5Context ;
+
+/* raw routines */
+void md5_init (MD5Context *ctx);
+void md5_update (MD5Context *ctx, const void *buf, size_t len);
+void md5_final (MD5Context *ctx, unsigned char digest[16]);
+
+
+#endif /* MD5_UTILS_H */
diff --git a/doc/api.xml b/doc/api.xml
new file mode 100644
index 0000000..7a49ee2
--- /dev/null
+++ b/doc/api.xml
@@ -0,0 +1,2360 @@
+<?xml version="1.0"?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "/usr/local/share/docbook-dtd/4.1.2/docbookx.dtd" [
+]>
+<article id="index">
+
+ <articleinfo>
+ <authorgroup>
+ <author>
+ <firstname>Brian</firstname> <surname>Stafford</surname>
+ <affiliation>
+ <address>
+ <email>brian@stafford.uklinux.org</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+
+ <copyright>
+ <year>2001,2002</year>
+ <holder>Brian Stafford</holder>
+ </copyright>
+
+ <legalnotice>
+ <para>
+Permission is granted to copy, distribute and/or modify this document
+under the terms of the <citetitle>GNU Free Documentation
+License</citetitle>, Version 1.1 or any later version published by the
+Free Software Foundation with no Invariant Sections, no Front-Cover
+Texts, and no Back-Cover Texts. You may obtain a copy of the
+<citetitle>GNU Free Documentation License</citetitle> from the Free
+Software Foundation by visiting <ulink type="http"
+url="http://www.fsf.org">their Web site</ulink> or by writing to: Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.
+ </para>
+ </legalnotice>
+
+ <title>libESMTP API Reference</title>
+
+ <abstract>
+ <para>
+This document describes the libESMTP programming interface. The libESMTP
+API is intended for use as an ESMTP client within a Mail User Agent
+(MUA) or other program that wishes to submit mail to a preconfigured
+Message Submission Agent (MSA).
+ </para>
+ </abstract>
+ </articleinfo>
+
+<!-- ====================================================================== -->
+
+ <sect1 id="important">
+ <title>Important</title>
+ <para>
+libESMTP is currently under development. Consequently certain parts
+of the API are subject to change between releases of the library and
+certain APIs and SMTP extensions have not yet been fully implemented.
+ </para>
+
+ <para>
+Furthermore, the descriptions in this document do not necessarily reflect
+currently implemented functionality. For now, regard this document more
+as a statement of intent than an accurate description, or to quote
+<citetitle pubwork="book">The Hitch Hikers' Guide to the Galaxy</citetitle>,
+<quote>Much of it is apocryphal or, at the very least, wildly
+inaccurate</quote>. However, where it is inaccurate, it is
+<quote>definitively inaccurate</quote>.
+ </para>
+
+ <para>
+If in doubt, consult the source. The API and documentation will stabilise
+as version 1.0 approaches.
+ </para>
+
+ <para>
+ </para>
+ </sect1>
+
+<!-- ====================================================================== -->
+
+ <sect1 id="intro">
+ <title>Introduction</title>
+ <para>
+For the most part, the libESMTP API is a relatively thin layer over SMTP
+protocol operations, that is, most API functions and their arguments are
+similar to the corresponding protocol commands. Further API functions
+manage a callback mechanism which is used to read messages from the
+application and to report protocol progress to the application. The
+remainder of the API is devoted to reporting on a completed SMTP session.
+ </para>
+
+ <para>
+Although the API closely models the protocol itself, libESMTP relieves the
+programmer of all the work needed to properly implement RFC 2821 (formerly
+RFC 821) and avoids many of the pitfalls in typical SMTP implementations.
+It constructs SMTP commands, parses responses, provides socket buffering
+and pipelining, where appropriate provides for TLS connections and
+provides a failover mechanism based on DNS allowing multiple redundant
+MSAs. Furthermore, support for the SMTP extension mechanism is
+incorporated by design rather than as an afterthought.
+ </para>
+
+ <para>
+There is limited support for processing RFC 2822 message headers. This
+is intended to ensure that messages copied to the SMTP server have their
+correct complement of headers. Headers that should not be present are
+stripped and reasonable defaults are provided for missing headers. In
+addition, the header API allows the defaults to be tuned and provides a
+mechanism to specify message headers when this might be difficult to do
+directly in the message data.
+ </para>
+
+ <para>
+libESMTP does not implement MIME [RFC 2045] since MIME is orthogonal
+to RFC 2822. It is expected that a separate library will be used to
+construct MIME documents. libESMTP ensures that top level MIME headers
+are passed unaltered and the header API functions are guaranteed to fail
+if any header in the name space reserved for MIME is specified, thus
+ensuring that MIME documents are not accidentally corrupted.
+ </para>
+ </sect1>
+
+ <sect1 id="smtp-extensions">
+ <title>SMTP Extensions</title>
+
+ <para>
+libESMTP supports the following SMTP extensions. Those extensions which
+are useful only in Message Transport Agents (MTA) will not be implemented
+in libESMTP.
+ </para>
+
+ <itemizedlist>
+ <listitem><simpara>[RFC 1893] ENHANCEDSTATUSCODES</simpara></listitem>
+ <listitem><simpara>[RFC 2920] PIPELINING</simpara></listitem>
+ <listitem><simpara>[RFC 1891] DSN</simpara></listitem>
+ <listitem><simpara>[RFC 2554] AUTH (SASL)</simpara></listitem>
+ <listitem><simpara>[RFC 2487] STARTTLS</simpara></listitem>
+ <listitem><simpara>[RFC 1870] SIZE</simpara></listitem>
+ <listitem><simpara>[RFC 1985] ETRN</simpara></listitem>
+ <listitem><simpara>[RFC 1652] 8BITMIME</simpara></listitem>
+ <listitem><simpara>[RFC 2852] DELIVERBY</simpara></listitem>
+ </itemizedlist>
+
+ <para>
+Supports is planned for the following SMTP extensions. As new SMTP
+extensions are defined, they will be implemented if they are relevant
+for use in programs that submit mail.
+ </para>
+
+ <itemizedlist>
+ <listitem><simpara>[RFC 3030] CHUNKING</simpara></listitem>
+ <listitem><simpara>[RFC 3030] BINARYMIME</simpara></listitem>
+ </itemizedlist>
+
+ <para>
+Please note that certain of the SMTP extensions are processed internally
+to libESMTP and do not require corresponding API functions.
+ </para>
+ </sect1>
+
+ <sect1 id="api">
+ <title>API</title>
+
+ <para>
+The libESMTP API is intended to be a relatively small and lightweight
+interface to the SMTP protocol and its extensions. However, given the
+number of API functions in libESMTP, this assertion is questionable. In
+mitigation, many of these are necessary because the internal structures
+are opaque to the application and most of them are used to define the
+messages and recipients to be transferred to the SMTP server during the
+protocol session. Similarly a significant number of functions are used
+to query the status of the transfer after the event. The entire SMTP
+protocol session is performed by only one function call.
+ </para>
+
+ <sect2>
+ <title>Functions</title>
+ <programlisting>
+<!-- smtp_add_message -->smtp_message_t <link linkend="smtp_add_message">smtp_add_message</link> (smtp_session_t session);
+<!-- smtp_add_recipient -->smtp_recipient_t <link linkend="smtp_add_recipient">smtp_add_recipient</link> (smtp_message_t message, const char *mailbox);
+<!-- smtp_auth_set_context -->int <link linkend="smtp_auth_set_context">smtp_auth_set_context</link> (smtp_session_t session, auth_context_t context);
+<!-- smtp_create_session -->smtp_session_t <link linkend="smtp_create_session">smtp_create_session</link> (void);
+<!-- smtp_destroy_session -->int <link linkend="smtp_destroy_session">smtp_destroy_session</link> (smtp_session_t session);
+<!-- smtp_dsn_set_envid -->int <link linkend="smtp_dsn_set_envid">smtp_dsn_set_envid</link> (smtp_message_t message, const char *envid);
+<!-- smtp_dsn_set_notify -->int <link linkend="smtp_dsn_set_notify">smtp_dsn_set_notify</link> (smtp_recipient_t recipient, enum notify_flags flags);
+<!-- smtp_dsn_set_orcpt -->int <link linkend="smtp_dsn_set_orcpt">smtp_dsn_set_orcpt</link> (smtp_recipient_t recipient, const char *address_type, const char *address);
+<!-- smtp_dsn_set_ret -->int <link linkend="smtp_dsn_set_ret">smtp_dsn_set_ret</link> (smtp_message_t message, enum ret_flags flags);
+<!-- smtp_enumerate_messages -->int <link linkend="smtp_enumerate_messages">smtp_enumerate_messages</link> (smtp_session_t session, smtp_enumerate_messagecb_t cb, void *arg);
+<!-- smtp_enumerate_recipients -->int <link linkend="smtp_enumerate_recipients">smtp_enumerate_recipients</link> (smtp_message_t message, smtp_enumerate_recipientcb_t cb, void *arg);
+<!-- smtp_errno -->int <link linkend="smtp_errno">smtp_errno</link> (void);
+<!-- smtp_etrn_add_node -->smtp_etrn_node_t <link linkend="smtp_etrn_add_node">smtp_etrn_add_node</link> (smtp_session_t session, int option, const char *node);
+<!-- smtp_etrn_enumerate_nodes -->int <link linkend="smtp_etrn_enumerate_nodes">smtp_etrn_enumerate_nodes</link> (smtp_session_t session, smtp_etrn_enumerate_nodecb_t cb, void *arg);
+<!-- smtp_etrn_get_application_data -->void *<link linkend="smtp_etrn_set_application_data">smtp_etrn_get_application_data</link> (smtp_etrn_node_t node);
+<!-- smtp_etrn_node_status -->const smtp_status_t *<link linkend="smtp_etrn_node_status">smtp_etrn_node_status</link> (smtp_etrn_node_t node);
+<!-- smtp_etrn_set_application_data -->void *<link linkend="smtp_etrn_set_application_data">smtp_etrn_set_application_data</link> (smtp_etrn_node_t node, void *data);
+<!-- smtp_get_application_data -->void *<link linkend="smtp_set_application_data">smtp_get_application_data</link> (smtp_session_t session);
+<!-- smtp_message_get_application_data -->void *<link linkend="smtp_set_application_data">smtp_message_get_application_data</link> (smtp_message_t message);
+<!-- smtp_message_reset_status -->int <link linkend="smtp_message_reset_status">smtp_message_reset_status</link> (smtp_message_t recipient);
+<!-- smtp_message_set_application_data -->void *<link linkend="smtp_set_application_data">smtp_message_set_application_data</link> (smtp_message_t message, void *data);
+<!-- smtp_message_transfer_status -->const smtp_status_t *<link linkend="smtp_message_transfer_status">smtp_message_transfer_status</link> (smtp_message_t message);
+<!-- smtp_recipient_check_complete -->int <link linkend="smtp_recipient_check_complete">smtp_recipient_check_complete</link> (smtp_recipient_t recipient);
+<!-- smtp_recipient_get_application_data -->void *<link linkend="smtp_set_application_data">smtp_recipient_get_application_data</link> (smtp_recipient_t recipient);
+<!-- smtp_recipient_reset_status -->int <link linkend="smtp_recipient_reset_status">smtp_recipient_reset_status</link> (smtp_recipient_t recipient);
+<!-- smtp_recipient_set_application_data -->void *<link linkend="smtp_set_application_data">smtp_recipient_set_application_data</link> (smtp_recipient_t recipient, void *data);
+<!-- smtp_recipient_status -->const smtp_status_t *<link linkend="smtp_recipient_status">smtp_recipient_status</link> (smtp_recipient_t recipient);
+<!-- smtp_reverse_path_status -->const smtp_status_t *<link linkend="smtp_reverse_path_status">smtp_reverse_path_status</link> (smtp_message_t message);
+<!-- smtp_set_application_data -->void *<link linkend="smtp_set_application_data">smtp_set_application_data</link> (smtp_session_t session, void *data);
+<!-- smtp_set_eventcb -->int <link linkend="smtp_set_eventcb">smtp_set_eventcb</link> (smtp_session_t session, smtp_eventcb_t cb, void *arg);
+<!-- smtp_set_header -->int <link linkend="smtp_set_header">smtp_set_header</link> (smtp_message_t message, const char *header, ...);
+<!-- smtp_set_header_option -->int <link linkend="smtp_set_header_option">smtp_set_header_option</link> (smtp_message_t message, const char *header, enum header_option option, ...);
+<!-- smtp_set_hostname -->int <link linkend="smtp_set_hostname">smtp_set_hostname</link> (smtp_session_t session, const char *hostname);
+<!-- smtp_set_messagecb -->int <link linkend="smtp_set_messagecb">smtp_set_messagecb</link> (smtp_message_t message, smtp_messagecb_t cb, void *arg);
+<!-- smtp_set_monitorcb -->int <link linkend="smtp_set_monitorcb">smtp_set_monitorcb</link> (smtp_session_t session, smtp_monitorcb_t cb, void *arg, int headers);
+<!-- smtp_set_resent_headers -->int <link linkend="smtp_set_resent_headers">smtp_set_resent_headers</link> (smtp_message_t message, int onoff);
+<!-- smtp_set_reverse_path -->int <link linkend="smtp_set_reverse_path">smtp_set_reverse_path</link> (smtp_message_t message, const char *mailbox);
+<!-- smtp_set_server -->int <link linkend="smtp_set_server">smtp_set_server</link> (smtp_session_t session, const char *hostport);
+<!-- smtp_size_set_estimate -->int <link linkend="smtp_size_set_estimate">smtp_size_set_estimate</link> (smtp_message_t message, unsigned long size);
+<!-- smtp_start_session -->int <link linkend="smtp_start_session">smtp_start_session</link> (smtp_session_t session);
+<!-- smtp_starttls_enable -->int <link linkend="smtp_starttls_enable">smtp_starttls_enable</link> (smtp_session_t session, int how);
+<!-- smtp_starttls_set_ctx -->int <link linkend="smtp_starttls_set_ctx">smtp_starttls_set_ctx</link> (smtp_session_t session, SSL_CTX *ctx);
+<!-- smtp_strerror -->char *<link linkend="smtp_strerror">smtp_strerror</link> (int error, char buf[], size_t buflen);
+ </programlisting>
+ </sect2>
+
+ <sect2>
+ <title>Types</title>
+ <programlisting>
+<!-- smtp_enumerate_messagecb_t -->typedef void (*<link linkend="smtp_enumerate_messages">smtp_enumerate_messagecb_t</link>) (smtp_message_t message, void *arg);
+<!-- smtp_enumerate_recipientcb_t -->typedef void (*<link linkend="smtp_enumerate_recipients">smtp_enumerate_recipientcb_t</link>) (smtp_recipient_t recipient, const char *mailbox, void *arg);
+<!-- smtp_etrn_enumerate_nodecb_t -->typedef void (*<link linkend="smtp_etrn_enumerate_nodes">smtp_etrn_enumerate_nodecb_t</link>) (smtp_etrn_node_t node, int option, const char *domain, void *arg)
+<!-- smtp_etrn_node_t -->typedef struct smtp_etrn_node *<link linkend="smtp_etrn_add_node">smtp_etrn_node_t</link>;
+<!-- smtp_eventcb_t -->typedef void (*<link linkend="smtp_set_eventcb">smtp_eventcb_t</link>) (smtp_session_t session, smtp_message_t message, smtp_recipient_t recipient, int event_no, void *arg);
+<!-- smtp_messagecb_t -->typedef char *(*<link linkend="smtp_set_messagecb">smtp_messagecb_t</link>) (char **buf, int *len, void *arg);
+<!-- smtp_message_t -->typedef struct smtp_message *<link linkend="smtp_message_t">smtp_message_t</link>;
+<!-- smtp_monitorcb_t -->typedef void (*<link linkend="smtp_set_monitorcb">smtp_monitorcb_t</link>) (const char *buf, int buflen, int writing, void *arg);
+<!-- smtp_recipient_t -->typedef struct smtp_recipient *<link linkend="smtp_recipient_t">smtp_recipient_t</link>;
+<!-- smtp_session_t -->typedef struct smtp_session *<link linkend="smtp_session_t">smtp_session_t</link>;
+<!-- smtp_status_t -->typedef struct smtp_status <link linkend="smtp_status_t">smtp_status_t</link>;
+ </programlisting>
+ </sect2>
+
+ <sect2>
+ <title>Enumerations</title>
+ <programlisting>
+enum <link linkend="smtp_dsn_set_notify">notify_flags</link>;
+enum <link linkend="smtp_dsn_set_ret">ret_flags</link>;
+enum <link linkend="smtp_set_header_option">header_option</link>;
+ </programlisting>
+ </sect2>
+ </sect1>
+
+<!-- ====================================================================== -->
+
+ <sect1 id="reference">
+ <title>Reference</title>
+
+ <para>
+To use the libESMTP API, you must include libesmtp.h in all source
+files calling the API functions.
+ </para>
+
+ <para>
+Internally libESMTP creates and maintains three types of structures to
+build and track the state of an SMTP protocol session. Pointers
+to these structures are passed back to the application by the API and
+must be supplied in various other API calls.
+ </para>
+
+ <note>
+ <title>Opaque Pointers</title>
+ <para>
+All structures and pointers maintained by libESMTP are opaque, that is,
+the internal detail of libESMTP structures is not made available to the
+application.
+ </para>
+ </note>
+
+ <para>
+The pointers are declared as follows.
+ </para>
+
+ <programlisting>
+typedef struct smtp_session *<anchor id="smtp_session_t"/>smtp_session_t;
+typedef struct smtp_message *<anchor id="smtp_message_t"/>smtp_message_t;
+typedef struct smtp_recipient *<anchor id="smtp_recipient_t"/>smtp_recipient_t;
+ </programlisting>
+
+ <important>
+ <title>Signal Handling</title>
+ <para>
+It is advisable for your application to catch or ignore SIGPIPE.
+libESMTP sets timeouts as it progresses through the protocol. In
+addition the remote server might close its socket at any time.
+Consequently libESMTP may sometimes try to write to a socket with no
+reader. Catching or ignoring SIGPIPE ensures the application isn't
+killed accidentally when this happens during the protocol session.
+ </para>
+
+ <para>
+Code similar to the following may be used to do this.
+ </para>
+ <programlisting>
+#include &lt;signal.h&gt;
+
+void
+ignore_sigpipe (void)
+{
+ struct sigaction sa;
+
+ sa.sa_handler = SIG_IGN;
+ sigemptyset (&amp;sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction (SIGPIPE, &amp;sa, NULL);
+}
+ </programlisting>
+ </important>
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_create_session">
+ <refnamediv>
+ <refname>smtp_create_session</refname>
+ <refpurpose>create SMTP session descriptor</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>smtp_session_t <function>smtp_create_session</function></funcdef>
+ <void/>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Create a descriptor which maintains internal state for the SMTP session.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+The descriptor for the SMTP session or <constant>NULL</constant> on failure.
+ </para>
+ </refsect1>
+ </refentry>
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_add_message">
+ <refnamediv>
+ <refname>smtp_add_message</refname>
+ <refpurpose>add a message to an SMTP session</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>smtp_message_t <function>smtp_add_message</function></funcdef>
+ <paramdef>smtp_session_t <parameter>session</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Add a message to the list of messages to be transferred to the remote
+MTA during an SMTP session.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+The descriptor for the message state, or <constant>NULL</constant> on failure.
+ </para>
+ </refsect1>
+ </refentry>
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_enumerate_messages">
+ <refnamediv>
+ <refname>smtp_enumerate_messages</refname>
+ <refpurpose>enumerate messages in an SMTP session</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>typedef void <function>(*smtp_enumerate_messagecb_t)</function></funcdef>
+ <paramdef>smtp_message_t <parameter>message</parameter></paramdef>
+ <paramdef>void *<parameter>arg</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int <function>smtp_enumerate_messages</function></funcdef>
+ <paramdef>smtp_session_t <parameter>session</parameter></paramdef>
+ <paramdef>smtp_enumerate_messagecb_t <parameter>cb</parameter></paramdef>
+ <paramdef>void *<parameter></parameter>arg</paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Call the callback function once for each message in an smtp session.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+Zero on failure, non-zero on success.
+ </para>
+ </refsect1>
+ </refentry>
+
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_set_hostname">
+ <refnamediv>
+ <refname>smtp_set_hostname</refname>
+ <refpurpose>set the name of the localhost</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int <function>smtp_set_hostname</function></funcdef>
+ <paramdef>smtp_session_t <parameter>session</parameter></paramdef>
+ <paramdef>const char *<parameter>hostname</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Set the name of the localhost. If one is not specified, the local host
+name will be determined using <function>uname</function>.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+Zero on failure, non-zero on success.
+ </para>
+ </refsect1>
+ </refentry>
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_set_server">
+ <refnamediv>
+ <refname>smtp_set_server</refname>
+ <refpurpose>set the hostname of the message submission server</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef><function>smtp_set_server</function></funcdef>
+ <paramdef>smtp_session_t <parameter>session</parameter></paramdef>
+ <paramdef>const char *<parameter>hostport</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Set the host name and service for the client connection. This is specified
+in the format <userinput>host.example.org[:service]</userinput> with no
+whitespace surrounding the colon if <token>service</token> is specified.
+<token>service</token> may be a name from
+<filename>/etc/services</filename> or a decimal port number. If not
+specified the port defaults to 587. Host and service name validity is
+not checked until an attempt to connect to the remote host.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+Zero on failure, non-zero on success.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Notes</title>
+ <para>
+The default port number is set to 587 since this is the port that should
+be used for mail submission, see RFC 2476. By choosing this default
+now, the API does not change behaviour unexpectedly in the future as
+use of the new standard becomes commonplace. The hostport nototion
+simplifies things for the application, the user can type
+<userinput>localhost:smtp</userinput> or
+<userinput>localhost:25</userinput> where the application expects a host
+name.
+ </para>
+ </refsect1>
+ </refentry>
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_set_reverse_path">
+ <refnamediv>
+ <refname>smtp_set_reverse_path</refname>
+ <refpurpose>set the reverse path mailbox</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef><function>smtp_set_reverse_path</function></funcdef>
+ <paramdef>smtp_message_t <parameter>message</parameter></paramdef>
+ <paramdef>const char *<parameter>mailbox</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Set the reverse path (envelope sender) mailbox address.
+<parameter>mailbox</parameter> must be an address using the syntax
+specified in RFC 2821. If a null reverse path is required, specify
+<parameter>mailbox</parameter> as <constant>NULL</constant> or
+<constant>""</constant>.
+ </para>
+ <para>
+If the value is a non-empty string and neither the message contains
+a From: header nor a From: is specified using
+<function>smtp_set_header</function>, the reverse path mailbox address
+specified with this API will be used to generate one.
+ </para>
+ <para>
+It is strongly reccommended that the message supplies a From:
+header specifying a single mailbox or a Sender: header and
+a From: header specifying multiple mailboxes or that the
+libESMTP header APIs are used to create them.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+Zero on failure, non-zero on success.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Notes</title>
+ <para>
+Not calling this API has the same effect as specifing
+<parameter>mailbox</parameter> as <constant>NULL</constant>.
+ </para>
+ </refsect1>
+ </refentry>
+
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_add_recipient">
+ <refnamediv>
+ <refname>smtp_add_recipient</refname>
+ <refpurpose>add a recipient to a message</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>smtp_recipient_t <function>smtp_add_recipient</function></funcdef>
+ <paramdef>smtp_message_t <parameter>message</parameter></paramdef>
+ <paramdef>const char *<parameter>mailbox</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Add a recipient to the message. <parameter>mailbox</parameter> must be an address
+using the syntax specified in RFC 2821.
+ </para>
+
+ <para>
+If neither the message contains a To: header nor a
+To: is specified using <function>smtp_set_header</function>, a
+To: header will be automatically generated using the list
+of envelope recipients.
+ </para>
+
+ <para>
+It is strongly reccommended that the message supplies To:,
+Cc: and Bcc: headers or that the
+libESMTP header APIs are used to create them.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+The descriptor for the recipient state or <constant>NULL</constant> for failure.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Notes</title>
+ <para>
+The envelope recipient need not be related to the To/Cc/Bcc
+recipients, for example, when a mail is resent to the recipients
+of a mailing list or as a result of alias expansion.
+ </para>
+ </refsect1>
+ </refentry>
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_enumerate_recipients">
+ <refnamediv>
+ <refname>smtp_enumerate_recipients</refname>
+ <refpurpose>enumerate message recipients</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>typedef void (*<function>smtp_enumerate_recipientcb_t</function>)</funcdef>
+ <paramdef>smtp_recipient_t <parameter>recipient</parameter></paramdef>
+ <paramdef>const char *<parameter>mailbox</parameter></paramdef>
+ <paramdef>void *<parameter>arg</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int <function>smtp_enumerate_recipients</function></funcdef>
+ <paramdef>smtp_message_t <parameter>message</parameter></paramdef>
+ <paramdef>smtp_enumerate_recipientcb_t <parameter>cb</parameter></paramdef>
+ <paramdef>void *<parameter>arg</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Call the callback function once for each recipient in the SMTP
+message.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+Zero on failure, non-zero on success.
+ </para>
+ </refsect1>
+ </refentry>
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_set_header">
+ <refnamediv>
+ <refname>smtp_set_header</refname>
+ <refpurpose>set an RFC 2822 message header</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int <function>smtp_set_header</function></funcdef>
+ <paramdef>smtp_message_t <parameter>message</parameter></paramdef>
+ <paramdef>const char *<parameter>header</parameter></paramdef>
+ <paramdef><parameter>...</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Set an RFC 2822 message header.
+ </para>
+ <para>
+If a value is supplied using <function>smtp_set_header</function> the header
+is required to be present in the message. If the <constant>Hdr_OVERRIDE</constant>
+option is not set a value supplied in the message is used unchanged;
+otherwise the value in the message is replaced.
+ </para>
+ <para>
+Headers in the message not corresonding to a default header action or set
+with <function>smtp_set_header</function> are passed unchanged.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+Zero on failure, non-zero on success.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Headers</title>
+ <para>
+This section lists the additional function arguments for individual
+RFC 2822 headers.
+ </para>
+
+ <variablelist>
+ <title>Function Arguments</title>
+ <varlistentry>
+ <term>default</term>
+ <listitem>
+ <programlisting>const char *value</programlisting>
+ <para>
+Headers not specifically known to libESMTP are treated as
+simple string values.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>Date:</term>
+ <listitem>
+ <programlisting>const time_t *value</programlisting>
+ <para>
+A pointer to a <type>time_t</type> supplies the value for this header.
+The time pointed to is copied and is formatted according to RFC 2822 when
+the value is required.
+ </para>
+ <para>
+The Date: header is automatically generated if one is not
+supplied either in the message or via the API.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>Message-Id:</term>
+ <listitem>
+ <programlisting>const char *value</programlisting>
+ <para>
+A string value is supplied which is the message identifier.
+If <constant>NULL</constant> is supplied, a value is automatically generated.
+At present there is no way for the application to retrieve the
+automatically generated value.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>From:</term>
+ <term>Disposition-Notification-To:</term>
+ <listitem>
+ <programlisting>const char *phrase, const char *mailbox</programlisting>
+ <para>
+<parameter>phrase</parameter> is free format text which is usually the
+real name of the recipient specified in <parameter>mailbox</parameter>, for example
+Brian Stafford.
+ </para>
+ <para>
+<parameter>mailbox</parameter> is as defined in RFC 2822, for example
+brian@stafford.uklinux.net.
+ </para>
+ <para>
+The From: header is automatically generated if one is not
+supplied either in the message or via the API.
+Refer to <function>smtp_set_reverse_path</function> for a
+description of the action taken by libESMTP.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>To:</term>
+ <term>Cc:</term>
+ <term>Bcc:</term>
+ <term>Reply-To:</term>
+ <listitem>
+ <programlisting>const char *phrase, const char *address</programlisting>
+ <para>
+<parameter>phrase</parameter> is free format text which is usually the
+real name of the recipient specified in <parameter>address</parameter>,
+for example Brian Stafford.
+ </para>
+ <para>
+<parameter>address</parameter> is as defined in RFC 2822, for example
+brian@stafford.uklinux.net.
+These headers may be set multiple times, however only one copy of each
+header listing all the values is actually created in the message.
+ </para>
+ <para>
+The To: header is automatically generated if one is not
+supplied either in the message or via the API. Refer to
+<function>smtp_add_recipient</function> for a description of the action taken
+by libESMTP.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Notes</title>
+ <para>
+Certain headers may not be set using this call. In particular, MIME
+headers cannot be set, the values in the message are always used.
+This is because, in the words of RFC 2045, MIME (RFC 2045 -
+RFC 2049) is &quot;orthogonal&quot; to RFC 2822 and
+libESMTP strives to preserve this condition.
+In addition, headers added to a message at delivery time such as
+Return-Path: are always deleted from the message.
+ </para>
+ <para>
+Certain headers may be specified multiple times and generate a single
+header listing all values specified. If the header does not permit a
+list of values, calls to <function>smtp_set_header</function> but the
+first for a given header will fail.
+ </para>
+ </refsect1>
+ </refentry>
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_set_header_option">
+ <refnamediv>
+ <refname>smtp_set_header_option</refname>
+ <refpurpose>set options for message header processing</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int <function>smtp_set_header_option</function></funcdef>
+ <paramdef>smtp_message_t <parameter>message</parameter></paramdef>
+ <paramdef>const char *<parameter>header</parameter></paramdef>
+ <paramdef>enum header_option <parameter>option</parameter></paramdef>
+ <paramdef><parameter>...</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <programlisting>
+enum header_option
+ {
+ Hdr_OVERRIDE,
+ Hdr_PROHIBIT
+ };
+ </programlisting>
+ <para>
+Set an RFC 2822 message header option.
+ </para>
+ <variablelist>
+ <title>Header Options</title>
+ <varlistentry>
+ <term>Hdr_OVERRIDE</term>
+ <listitem>
+ <para>
+Normally, a header set by <function>smtp_set_header</function> is used
+as a default if one is not supplied in the message. When Hdr_OVERRIDE
+is set, the value supplied in the API overrides the value in the
+message.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Hdr_PROHIBIT</term>
+ <listitem>
+ <para>
+libESMTP generates certain headers automatically if not present in the
+message. This is the default behaviour for headers that are RECOMMENDED
+but not REQUIRED by RFC 2822, such as Message-Id:. Setting Hdr_PROHIBIT
+ensures the transmitted message does not contain the named header. If the
+header is REQUIRED by RFC 2822, the API will fail.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+Zero on failure, non-zero on success.
+ </para>
+ </refsect1>
+ </refentry>
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_set_resent_headers">
+ <refmeta>
+ <refentrytitle>smtp_set_resent_headers</refentrytitle>
+ </refmeta>
+
+ <refnamediv>
+ <refname>smtp_set_resent_headers</refname>
+ <refpurpose></refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int <function>smtp_set_resent_headers</function></funcdef>
+ <paramdef>smtp_message_t <parameter>message</parameter></paramdef>
+ <paramdef>int <parameter>onoff</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Request special processing of headers which have a Resent-
+variation. This option is used when resending messages as described in
+RFC 2822.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+Zero on failure, non-zero on success.
+ </para>
+ </refsect1>
+ </refentry>
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_set_messagecb">
+ <refnamediv>
+ <refname>smtp_set_messagecb</refname>
+ <refpurpose>set callback for reading the message from an application</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>typedef const char *(<function>smtp_messagecb_t</function>)</funcdef>
+ <paramdef>void **<parameter>buf</parameter></paramdef>
+ <paramdef>int *<parameter>len</parameter></paramdef>
+ <paramdef>void *<parameter>arg</parameter></paramdef>
+ </funcprototype>
+ <funcprototype>
+ <funcdef>int <function>smtp_set_messagecb</function></funcdef>
+ <paramdef>smtp_message_t <parameter>message</parameter></paramdef>
+ <paramdef>smtp_messagecb_t <parameter>cb</parameter></paramdef>
+ <paramdef>void *<parameter>arg</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Set a callback function to read an RFC 2822 formatted message
+from the application.
+ </para>
+
+ <para>
+The callback is used for two purposes. If <parameter>len</parameter> is
+set to <constant>NULL</constant> the callback is to rewind the message.
+The return value is not used. If <parameter>len</parameter> is not
+<constant>NULL</constant>, the callback returns a pointer to the start
+of the message buffer and sets the <parameter>*len</parameter> to the
+number of octets of data in the buffer.
+ </para>
+
+ <para>
+The callback is called repeatedly until the entire message has
+been processed. When all the message data has been read the
+callback should return <constant>NULL</constant>.
+ </para>
+
+ <para>
+If the callback requires a buffer for the message data, it should
+allocate one with malloc and place the pointer in <parameter>*buf</parameter>,
+otherwise <parameter>*buf</parameter> must be set to <constant>NULL</constant>. The buffer is freed
+automatically by libESMTP.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+Zero on failure, non-zero on success.
+ </para>
+ </refsect1>
+ </refentry>
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_set_eventcb">
+ <refnamediv>
+ <refname>smtp_set_eventcb</refname>
+ <refpurpose>set callback for reporting events to the applciation</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>typedef void (*<function>smtp_eventcb_t</function>)</funcdef>
+ <paramdef>smtp_session_t <parameter>session</parameter></paramdef>
+ <paramdef>int <parameter>event_no</parameter></paramdef>
+ <paramdef>void *<parameter>arg</parameter></paramdef>
+ <paramdef><parameter>...</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int <function>smtp_set_eventcb</function></funcdef>
+ <paramdef>smtp_session_t <parameter>session</parameter></paramdef>
+ <paramdef>smtp_eventcb_t <parameter>cb</parameter></paramdef>
+ <paramdef>void *<parameter>arg</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ <programlisting>
+enum
+ {
+ /* Protocol progress */
+ SMTP_EV_CONNECT,
+ SMTP_EV_MAILSTATUS,
+ SMTP_EV_RCPTSTATUS,
+ SMTP_EV_MESSAGEDATA,
+ SMTP_EV_MESSAGESENT,
+ SMTP_EV_DISCONNECT,
+
+ /* Protocol extension progress */
+ SMTP_EV_ETRNSTATUS = 1000,
+
+ /* Required extensions */
+ SMTP_EV_EXTNA_DSN = 2000,
+ SMTP_EV_EXTNA_8BITMIME,
+ SMTP_EV_EXTNA_STARTTLS,
+ SMTP_EV_EXTNA_ETRN,
+
+ /* Extensions specific events */
+ SMTP_EV_DELIVERBY_EXPIRED = 3000,
+
+ /* STARTTLS */
+ SMTP_EV_WEAK_CIPHER = 3100,
+ SMTP_EV_STARTTLS_OK,
+ SMTP_EV_INVALID_PEER_CERTIFICATE,
+ SMTP_EV_NO_PEER_CERTIFICATE,
+ SMTP_EV_WRONG_PEER_CERTIFICATE
+ };
+ </programlisting>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Set a callback function to process protocol events during the SMTP session
+with the server. The callback function is called when something significant
+happens, such as when server, message or recipient status codes are notified.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+Zero on failure, non-zero on success.
+ </para>
+ </refsect1>
+ </refentry>
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_set_monitorcb">
+ <refnamediv>
+ <refname>smtp_set_monitorcb</refname>
+ <refpurpose>set callback for tracing the SMTP protocol session</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>typedef void (*<function>smtp_monitorcb_t</function>)</funcdef>
+ <paramdef>const char *<parameter>buf</parameter></paramdef>
+ <paramdef>int <parameter>buflen</parameter></paramdef>
+ <paramdef>int <parameter>writing</parameter></paramdef>
+ <paramdef>void *<parameter>arg</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int <function>smtp_set_monitorcb</function></funcdef>
+ <paramdef>smtp_session_t <parameter>session</parameter></paramdef>
+ <paramdef>smtp_monitorcb_t <parameter>cb</parameter></paramdef>
+ <paramdef>void *<parameter>arg</parameter></paramdef>
+ <paramdef>int <parameter>headers</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ <programlisting>
+/* Protocol monitor callback. Values for writing */
+#define SMTP_CB_READING 0
+#define SMTP_CB_WRITING 1
+#define SMTP_CB_HEADERS 2
+ </programlisting>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Set a callback function to monitor the SMTP session with the server.
+The callback is called with packets of data either transmitted to or
+received from the server. When writing is non-zero, data
+is being written to the remote host otherwise the data is being read
+from the remote host. In the event that an encrypted connection to the
+server is in use, the monitor callback will show the clear text.
+ </para>
+ <para>
+When the callback is used, the data passed in <parameter>buf</parameter>
+corresponds to the buffered packet of data written to or read from the
+server. This may contain more than one protocol command or response.
+ </para>
+ <para>
+The content of the DATA (or BDAT) command is not passed back to the
+application since this is typically large and possibly binary. However,
+it may be useful to view the message headers. If
+<parameter>headers</parameter> is non-zero the callback will be used to
+display the message headers. In this case, the value of
+<parameter>writing</parameter> is set to <constant>CB_HEADERS</constant>
+(2) instead of <constant>CB_WRITING</constant> (1) so that the
+application can distinguish headers from other data sent to the SMTP
+server. The callback is passed each header one at a time and in this
+case the data passed back to the application does not reflect the actual
+buffering of the data on-the-wire.
+ </para>
+ <para>
+Note that headers within MIME parts will not be returned,
+only the message headers.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+Zero on failure, non-zero on success.
+ </para>
+ </refsect1>
+ </refentry>
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_set_application_data">
+ <refnamediv>
+ <refname>smtp_set_application_data</refname>
+ <refpurpose>attach application data to libESMTP structures</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void *<function>smtp_set_application_data</function></funcdef>
+ <paramdef>smtp_session_t <parameter>session</parameter></paramdef>
+ <paramdef>void *<parameter>data</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void *<function>smtp_get_application_data</function></funcdef>
+ <paramdef>smtp_session_t <parameter>session</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void *<function>smtp_message_set_application_data</function></funcdef>
+ <paramdef>smtp_message_t <parameter>message</parameter></paramdef>
+ <paramdef>void *<parameter>data</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void *<function>smtp_message_get_application_data</function></funcdef>
+ <paramdef>smtp_message_t <parameter>message</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void *<function>smtp_recipient_set_application_data</function></funcdef>
+ <paramdef>smtp_recipient_t <parameter>recipient</parameter></paramdef>
+ <paramdef>void *<parameter>data</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void *<function>smtp_recipient_get_application_data</function></funcdef>
+ <paramdef>smtp_recipient_t <parameter>recipient</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+These functions associate application defined data with each of the opaque
+structures. The set variants of the functions set a new value for the
+application data and return the old value in their respective structures.
+The get variants return the current value of the application data.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+ </para>
+ </refsect1>
+ </refentry>
+
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_start_session">
+ <refnamediv>
+ <refname>smtp_start_session</refname>
+ <refpurpose>run an SMTP protocol session</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int <function>smtp_start_session</function></funcdef>
+ <paramdef>smtp_session_t <parameter>session</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Initiate a mail submission session with an SMTP server.
+ </para>
+
+ <para>
+This connects to an SMTP server and transfers the messages in
+the session. The SMTP envelope is constructed using the message
+and recipient parameters set up previously. The message
+callback is then used to read the message contents to the
+server. As the RFC 2822 headers are read from the application,
+they may be processed. Header processing terminates when the
+first line containing only CR-LF is encountered. The remainder of
+the message is copied verbatim.
+ </para>
+
+ <para>
+This call is atomic in the sense that a connection to the server
+is made only when this is called and is closed down before it
+returns, i.e. there is no connection to the server outside this
+function.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+ </para>
+ </refsect1>
+ </refentry>
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_destroy_session">
+ <refnamediv>
+ <refname>smtp_destroy_session</refname>
+ <refpurpose>release all resources associated with an SMTP session</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int <function>smtp_destroy_session</function></funcdef>
+ <paramdef>smtp_session_t <parameter>session</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Deallocate all resources associated with the SMTP session.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+ </para>
+ </refsect1>
+ </refentry>
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_version">
+ <refnamediv>
+ <refname>smtp_version</refname>
+ <refpurpose>retrieve libESMTP version information</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int <function>smtp_version</function></funcdef>
+ <paramdef>void *<parameter>buf</parameter></paramdef>
+ <paramdef>size_t <parameter>len</parameter></paramdef>
+ <paramdef>int <parameter>what</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Retrieve version information for the libESMTP in use.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+Zero on failure, non-zero on success.
+ </para>
+ </refsect1>
+ </refentry>
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_errno">
+ <refnamediv>
+ <refname>smtp_errno</refname>
+ <refpurpose>get error code for most recent API call</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int <function>smtp_errno</function></funcdef>
+ <paramdef><parameter></parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Retrieve the error code for the most recently failed API in the
+calling thread.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+libESMTP error code.
+ </para>
+ </refsect1>
+
+<!-- ====================================================================== -->
+
+ <refsect1 id="errorcodes">
+ <title>Error Codes</title>
+
+ <programlisting>
+#define SMTP_ERR_NOTHING_TO_DO 2
+#define SMTP_ERR_DROPPED_CONNECTION 3
+#define SMTP_ERR_INVALID_RESPONSE_SYNTAX 4
+#define SMTP_ERR_STATUS_MISMATCH 5
+#define SMTP_ERR_INVALID_RESPONSE_STATUS 6
+#define SMTP_ERR_INVAL 7
+#define SMTP_ERR_EXTENSION_NOT_AVAILABLE 8
+
+/* libESMTP versions of some getaddrinfo error numbers */
+#define SMTP_ERR_EAI_ADDRFAMILY 9
+#define SMTP_ERR_EAI_NODATA 10
+#define SMTP_ERR_EAI_FAIL 11
+#define SMTP_ERR_EAI_AGAIN 12
+#define SMTP_ERR_EAI_MEMORY 13
+#define SMTP_ERR_EAI_FAMILY 14
+#define SMTP_ERR_EAI_BADFLAGS 15
+#define SMTP_ERR_EAI_NONAME 16
+#define SMTP_ERR_EAI_SERVICE 17
+#define SMTP_ERR_EAI_SOCKTYPE 18
+ </programlisting>
+
+ <para>
+SMTP_ERR_INVAL means that an API was called with invalid arguments.
+ </para>
+ </refsect1>
+ </refentry>
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_strerror">
+ <refnamediv>
+ <refname>smtp_strerror</refname>
+ <refpurpose>convert error code to English text</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>const char *<function>smtp_strerror</function></funcdef>
+ <paramdef>int <parameter>error</parameter></paramdef>
+ <paramdef>char <parameter>buf</parameter>[]</paramdef>
+ <paramdef>size_t <parameter>buflen</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Translate a libESMTP error number to a string suitable for use
+in an application error message. The resuting string is copied into
+<parameter>buf</parameter>.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+A pointer to the <parameter>buf</parameter> on success or NULL on failure.
+ </para>
+ </refsect1>
+ </refentry>
+
+ </sect1>
+
+<!-- ====================================================================== -->
+
+ <sect1 id="smtp_status_t">
+ <title>SMTP Status codes</title>
+
+ <para>
+Functions which retrieve SMTP status codes return a pointer to the following
+structure which contains the status information.
+ </para>
+
+ <programlisting>
+struct smtp_status
+ {
+ int code; /* SMTP protocol status pre */
+ const char *text; /* Text from the server */
+ int enh_class; /* RFC 2034 enhanced status triplet */
+ int enh_subject;
+ int enh_detail;
+ };
+typedef struct smtp_status smtp_status_t;
+ </programlisting>
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_message_transfer_status">
+ <refnamediv>
+ <refname>smtp_message_transfer_status</refname>
+ <refpurpose></refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>const smtp_status_t *<function>smtp_message_transfer_status</function></funcdef>
+ <paramdef>smtp_message_t <parameter>message</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Retrieve the message transfer success/failure status from a
+previous SMTP session. This includes SMTP status codes, RFC
+2034 enhanced status codes, if available, and text from the
+server describing the status. If a message is marked with a
+success or permanent failure status, it will not be resent if
+<function>smtp_start_session</function>
+is called again.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+<constant>NULL</constant> if no status information is available,
+otherwise a pointer to the status information. The pointer remains
+valid until the next call to libESMTP in the same thread.
+ </para>
+ </refsect1>
+ </refentry>
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_reverse_path_status">
+ <refnamediv>
+ <refname>smtp_reverse_path_status</refname>
+ <refpurpose></refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>const smtp_status_t *<function>smtp_reverse_path_status</function></funcdef>
+ <paramdef>smtp_message_t <parameter>message</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Retrieve the reverse path status from a previous SMTP session.
+This includes SMTP status codes, RFC 2034 enhanced status codes,
+if available, and text from the server describing the status.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+<constant>NULL</constant> if no status information is available, otherwise a pointer
+to the status information. The pointer remains valid until the
+next call to libESMTP in the same thread.
+ </para>
+ </refsect1>
+ </refentry>
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_message_reset_status">
+ <refmeta>
+ <refentrytitle>smtp_message_reset_status</refentrytitle>
+ </refmeta>
+
+ <refnamediv>
+ <refname>smtp_message_reset_status</refname>
+ <refpurpose></refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int <function>smtp_message_reset_status</function></funcdef>
+ <paramdef>smtp_message_t <parameter>recipient</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Reset the message status to the state it would have before
+<function>smtp_start_session</function> is called for the first time on the
+containing session. This may be used to force libESMTP to
+resend certain messages.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+Zero on failure, non-zero on success.
+ </para>
+ </refsect1>
+ </refentry>
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_recipient_status">
+ <refnamediv>
+ <refname>smtp_recipient_status</refname>
+ <refpurpose></refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>const smtp_status_t *<function>smtp_recipient_status</function></funcdef>
+ <paramdef>smtp_recipient_t <parameter>recipient</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Retrieve the recipient success/failure status from a previous SMTP
+session. This includes SMTP status codes, RFC 2034 enhanced status
+codes, if available and text from the server describing the status.
+If a recipient is marked with a success or permanent failure status,
+it will not be resent if <function>smtp_start_session</function> is
+called again, however it may be used when generating To: or Cc: headers
+if required.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+<constant>NULL</constant> if no status information is available, otherwise a pointer
+to the status information. The pointer remains valid until the
+next call to libESMTP in the same thread.
+ </para>
+ </refsect1>
+ </refentry>
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_recipient_check_complete">
+ <refmeta>
+ <refentrytitle>smtp_recipient_check_complete</refentrytitle>
+ </refmeta>
+
+ <refnamediv>
+ <refname>smtp_recipient_check_complete</refname>
+ <refpurpose></refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int <function>smtp_recipient_check_complete</function></funcdef>
+ <paramdef>smtp_recipient_t <parameter>recipient</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Check whether processing is complete dor the specified recipient of the
+message. Processing is considered complete when an MTA has assumed
+responsibility for delivering the message, or if it has indicated a
+permanent failure.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+Zero if processing is not complete, non-zero otherwise.
+ </para>
+ </refsect1>
+ </refentry>
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_recipient_reset_status">
+ <refmeta>
+ <refentrytitle>smtp_recipient_reset_status</refentrytitle>
+ </refmeta>
+
+ <refnamediv>
+ <refname>smtp_recipient_reset_status</refname>
+ <refpurpose></refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int <function>smtp_recipient_reset_status</function></funcdef>
+ <paramdef>smtp_recipient_t <parameter>recipient</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Reset the recipient status to the state it would have before
+<function>smtp_start_session</function> is called for the first time on
+the containing session. This is used to force the libESMTP to resend
+previously successful recipients.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+Zero on failure, non-zero on success.
+ </para>
+ </refsect1>
+ </refentry>
+
+ </sect1>
+<!-- ====================================================================== -->
+
+ <sect1 id="extensions">
+ <title>SMTP Extensions</title>
+
+ <para>
+The following APIs relate to SMTP extensions. Note that not all
+supported extensions require corresponding API functions.
+ </para>
+ </sect1>
+
+ <sect1 id="dsn">
+ <title>RFC 1891. Delivery Status Notification (DSN)</title>
+
+ <para>
+ </para>
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_dsn_set_ret">
+ <refmeta>
+ <refentrytitle>smtp_dsn_set_ret</refentrytitle>
+ </refmeta>
+
+ <refnamediv>
+ <refname>smtp_dsn_set_ret</refname>
+ <refpurpose></refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int <function>smtp_dsn_set_ret</function></funcdef>
+ <paramdef>smtp_message_t <parameter>message</parameter></paramdef>
+ <paramdef>enum ret_flags <parameter>flags</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ <programlisting>
+enum ret_flags { Ret_NOTSET, Ret_FULL, Ret_HDRS };
+ </programlisting>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Instruct the reporting MTA whether to include the full content
+of the original message in the Delivery Status Notification, or
+just the headers.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+Non zero on success, zero on failure.
+ </para>
+ </refsect1>
+ </refentry>
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_dsn_set_envid">
+ <refmeta>
+ <refentrytitle>smtp_dsn_set_envid</refentrytitle>
+ </refmeta>
+
+ <refnamediv>
+ <refname>smtp_dsn_set_envid</refname>
+ <refpurpose></refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int <function>smtp_dsn_set_envid</function></funcdef>
+ <paramdef>smtp_message_t <parameter>message</parameter></paramdef>
+ <paramdef>const char *<parameter>envid</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Set the envelope identifier. This value is returned in the
+DSN and may be used by the MUA to associate the DSN with the
+message that caused it to be generated.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+Non zero on success, zero on failure.
+ </para>
+ </refsect1>
+ </refentry>
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_dsn_set_notify">
+ <refmeta>
+ <refentrytitle>smtp_dsn_set_notify</refentrytitle>
+ </refmeta>
+
+ <refnamediv>
+ <refname>smtp_dsn_set_notify</refname>
+ <refpurpose></refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int <function>smtp_dsn_set_notify</function></funcdef>
+ <paramdef>smtp_recipient_t <parameter>recipient</parameter></paramdef>
+ <paramdef>enum notify_flags <parameter>flags</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ <programlisting>
+enum notify_flags
+ {
+ Notify_NOTSET,
+ Notify_NEVER = -1,
+ Notify_SUCCESS = 1,
+ Notify_FAILURE = 2,
+ Notify_DELAY = 4
+ };
+ </programlisting>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Set the DSN notify options. Flags may be Notify_NOTSET
+or Notify_NEVER or any combination of Notify_SUCCESS,
+Notify_FAILURE and Notify_DELAY.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+Non zero on success, zero on failure.
+ </para>
+ </refsect1>
+ </refentry>
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_dsn_set_orcpt">
+ <refmeta>
+ <refentrytitle>smtp_dsn_set_orcpt</refentrytitle>
+ </refmeta>
+
+ <refnamediv>
+ <refname>smtp_dsn_set_orcpt</refname>
+ <refpurpose></refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int <function>smtp_dsn_set_orcpt</function></funcdef>
+ <paramdef>smtp_recipient_t <parameter>recipient</parameter></paramdef>
+ <paramdef>const char *<parameter>address_type</parameter></paramdef>
+ <paramdef>const char *<parameter>address</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Set the DSN ORCPT option.
+ </para>
+ <para>
+Included only for completeness. This DSN option is only used
+when performing mailing list expansion or similar situations
+when the envelope recipient no longer matches the recipient for
+whom the DSN is to be generated. Probably only useful to an MTA
+and should not normally be used by an MUA or other program which
+submits mail.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+Non zero on success, zero on failure.
+ </para>
+ </refsect1>
+ </refentry>
+
+ </sect1>
+<!-- ====================================================================== -->
+
+<sect1 id="size">
+<title>RFC 1870. SMTP Size Extension.</title>
+
+ <para>
+ </para>
+
+ <refentry id="smtp_size_set_estimate">
+ <refmeta>
+ <refentrytitle>smtp_size_set_estimate</refentrytitle>
+ </refmeta>
+
+ <refnamediv>
+ <refname>smtp_size_set_estimate</refname>
+ <refpurpose></refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int <function>smtp_size_set_estimate</function></funcdef>
+ <paramdef>smtp_message_t <parameter>message</parameter></paramdef>
+ <paramdef>unsigned long <parameter>size</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Used by the application to supply an estimate of the size of the
+message to be transferred.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+Non zero on success, zero on failure.
+ </para>
+ </refsect1>
+ </refentry>
+
+</sect1>
+<!-- ====================================================================== -->
+
+<sect1 id="e8bitmime">
+<title>RFC 1652. SMTP 8bit-MIME Transport Extension</title>
+
+ <para>
+ </para>
+
+ <refentry id="smtp_8bitmime_set_body">
+ <refmeta>
+ <refentrytitle>smtp_8bitmime_set_body</refentrytitle>
+ </refmeta>
+
+ <refnamediv>
+ <refname>smtp_8bitmime_set_body</refname>
+ <refpurpose></refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int <function>smtp_8bitmime_set_body</function></funcdef>
+ <paramdef>smtp_message_t <parameter>message</parameter></paramdef>
+ <paramdef>enum e8bitmime_body <parameter>body</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ <programlisting>
+enum e8bitmime_body
+ {
+ E8bitmime_NOTSET,
+ E8bitmime_7BIT,
+ E8bitmime_8BITMIME
+ };
+ </programlisting>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+The 8-bit MIME extension allows an SMTP client to declare the message
+body is either in strict conformance with RFC 2822
+(<constant>E8bitmime_7BIT</constant>) or that it is a MIME document
+where some or all of the MIME parts use 8bit encoding
+(<constant>E8bitmime_8BITMIME</constant>). If this API sets the body
+type to other than <constant>E8bitmime_NOTSET</constant>, libESMTP will
+use the event callback to notify the application if the MTA does not
+support the 8BITMIME extension.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+Non zero on success, zero on failure.
+ </para>
+ </refsect1>
+ </refentry>
+
+</sect1>
+
+<!-- ====================================================================== -->
+
+<sect1 id="etrn">
+<title>RFC 1985. Remote Message Queue Starting (ETRN)</title>
+
+
+<para>
+The SMTP ETRN extension is used to request a remore MTA to start its
+delivery queue for the specified domain. If the application requests
+the use if the ETRN extension and the remote MTA does not list ETRN,
+libESMTP will use the event callback to notify the application.
+</para>
+
+ <refentry id="smtp_etrn_add_node">
+ <refmeta>
+ <refentrytitle>smtp_etrn_add_node</refentrytitle>
+ </refmeta>
+
+ <refnamediv>
+ <refname>smtp_etrn_add_node</refname>
+ <refpurpose></refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <programlisting>
+typedef struct smtp_etrn_node *smtp_etrn_node_t;
+ </programlisting>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>smtp_etrn_node_t <function>smtp_etrn_add_node</function></funcdef>
+ <paramdef>smtp_session_t <parameter>session</parameter></paramdef>
+ <paramdef>int <parameter>option</parameter></paramdef>
+ <paramdef>const char *<parameter>node</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Add an ETRN node to the SMTP session.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+The descriptor for the ETRN node, or <constant>NULL</constant> on failure.
+ </para>
+ </refsect1>
+ </refentry>
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_etrn_enumerate_nodes">
+ <refmeta>
+ <refentrytitle>smtp_etrn_enumerate_nodes</refentrytitle>
+ </refmeta>
+
+ <refnamediv>
+ <refname>smtp_etrn_enumerate_nodes</refname>
+ <refpurpose></refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>typedef void (*<function>smtp_etrn_enumerate_nodecb_t</function>)</funcdef>
+ <paramdef>smtp_etrn_node_t <parameter>node</parameter></paramdef>
+ <paramdef>int <parameter>option</parameter></paramdef>
+ <paramdef>const char *<parameter>domain</parameter></paramdef>
+ <paramdef>void *<parameter>arg</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int <function>smtp_etrn_enumerate_nodes</function></funcdef>
+ <paramdef>smtp_session_t <parameter>session</parameter></paramdef>
+ <paramdef>smtp_etrn_enumerate_nodecb_t <parameter>cb</parameter></paramdef>
+ <paramdef>void *<parameter>arg</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Call the callback function once for each etrn node in the smtp session.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+Zero on failure, non-zero on success.
+ </para>
+ </refsect1>
+ </refentry>
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_etrn_node_status">
+ <refmeta>
+ <refentrytitle>smtp_etrn_node_status</refentrytitle>
+ </refmeta>
+
+ <refnamediv>
+ <refname>smtp_etrn_node_status</refname>
+ <refpurpose></refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>const smtp_status_t *<function>smtp_etrn_node_status</function></funcdef>
+ <paramdef>smtp_etrn_node_t <parameter>node</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Retrieve the ETRN node success/failure status from a previous SMTP
+session. This includes SMTP status codes, RFC 2034 enhanced status
+codes, if available and text from the server describing the status.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+<constant>NULL</constant> if no status information is available,
+otherwise a pointer to the status information. The pointer remains
+valid until the next call to libESMTP in the same thread.
+ </para>
+ </refsect1>
+ </refentry>
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_etrn_set_application_data">
+ <refmeta>
+ <refentrytitle>smtp_etrn_set_application_data</refentrytitle>
+ </refmeta>
+
+ <refnamediv>
+ <refname>smtp_etrn_set_application_data</refname>
+ <refpurpose></refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void *<function>smtp_etrn_set_application_data</function></funcdef>
+ <paramdef>smtp_etrn_node_t <parameter>node</parameter></paramdef>
+ <paramdef>void *<parameter>arg</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void *<function>smtp_etrn_get_application_data</function></funcdef>
+ <paramdef>smtp_etrn_node_t <parameter>node</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+These functions associate application defined data with the opaque
+ETRN structure. The set variant sets a new value for the
+application data and returns the old value for the application data.
+The get variant returns the current value of the application data.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+ </para>
+ </refsect1>
+ </refentry>
+
+</sect1>
+
+<!-- ====================================================================== -->
+
+<sect1 id="auth">
+<title>RFC 2554. SMTP Auth Extension</title>
+<para>
+</para>
+
+ <refentry id="smtp_auth_set_context">
+ <refmeta>
+ <refentrytitle>smtp_auth_set_context</refentrytitle>
+ </refmeta>
+
+ <refnamediv>
+ <refname>smtp_auth_set_context</refname>
+ <refpurpose></refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <programlisting>
+#include &lt;auth-client.h&gt;
+#include &lt;libesmtp.h&gt;
+ </programlisting>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int <function>smtp_auth_set_context</function></funcdef>
+ <paramdef>smtp_session_t <parameter>session</parameter></paramdef>
+ <paramdef>auth_context_t <parameter>context</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Enable the SMTP AUTH verb if <parameter>context</parameter> is not
+<constant>NULL</constant> or disable it when context is <constant>NULL</constant>.
+The authentication API is described separately.
+ </para>
+ <para>
+When enabled and the SMTP server advertises the AUTH extension, libESMTP
+will attempt to authenticate to the SMTP server before transferring
+any messages. context must be obtained from the SASL (RFC 2222) client
+library API defined in <filename>auth-client.h</filename>.
+ </para>
+
+ <note>
+ <title>Authentication Contexts</title>
+ <para>
+A separate authentication context must be created for each SMTP session.
+The application is responsible for destroying context. The application
+should either call <function>smtp_destroy_session</function> or call
+<function>smtp_auth_set_context</function> with context set to
+<constant>NULL</constant> before doing so.
+ </para>
+ </note>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+Non zero on success, zero on failure.
+ </para>
+ </refsect1>
+ </refentry>
+
+</sect1>
+<!-- ====================================================================== -->
+
+<sect1 id="starttls">
+<title>RFC 2487. SMTP StartTLS Extension</title>
+
+<para>
+If <ulink type="http" url="http://www.openssl.org/">OpenSSL</ulink> is
+available when building libESMTP, support for the STARTTLS extension can
+be enabled. If support is not enabled, the following APIs will always
+fail.
+</para>
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_starttls_enable">
+ <refmeta>
+ <refentrytitle>smtp_starttls_enable</refentrytitle>
+ </refmeta>
+
+ <refnamediv>
+ <refname>smtp_starttls_enable</refname>
+ <refpurpose></refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <programlisting>
+enum starttls_option
+ {
+ Starttls_DISABLED,
+ Starttls_ENABLED,
+ Starttls_REQUIRED
+ };
+ </programlisting>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int <function>smtp_starttls_enable</function></funcdef>
+ <paramdef>smtp_session_t <parameter>session</parameter></paramdef>
+ <paramdef>enum starttls_option <parameter>how</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Enable the SMTP STARTTLS verb if <parameter>how</parameter> is not
+<constant>Starttls_DISABLED</constant>. If set
+to <constant>Starttls_REQUIRED</constant> the protocol will quit rather
+than transferring any messages if the STARTTLS extension is not
+available.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+Non zero on success, zero on failure.
+ </para>
+ </refsect1>
+ </refentry>
+
+<!-- ====================================================================== -->
+
+ <refentry id="smtp_starttls_set_ctx">
+ <refmeta>
+ <refentrytitle>smtp_starttls_set_ctx</refentrytitle>
+ </refmeta>
+
+ <refnamediv>
+ <refname>smtp_starttls_set_ctx</refname>
+ <refpurpose></refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <programlisting>
+#include &lt;openssl/ssl.h&gt;
+#include &lt;libesmtp.h&gt;
+ </programlisting>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int <function>smtp_starttls_set_ctx</function></funcdef>
+ <paramdef>smtp_session_t <parameter>session</parameter></paramdef>
+ <paramdef>SSL_CTX *<parameter>ctx</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>
+Use an <type>SSL_CTX</type> created by the application. The
+<type>SSL_CTX</type> must be created by the application which is assumed
+to have initialised the OpenSSL library. If not used, OpenSSL is
+automatically initialised before calling any of the OpenSSL API
+functions.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+ <para>
+Non zero on success, zero on failure.
+ </para>
+ </refsect1>
+ </refentry>
+
+</sect1>
+
+</article>
diff --git a/errors.c b/errors.c
new file mode 100644
index 0000000..c327a55
--- /dev/null
+++ b/errors.c
@@ -0,0 +1,296 @@
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _SVID_SOURCE 1 /* Need this to get strerror_r() */
+
+#include <missing.h> /* declarations for missing library functions */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#if HAVE_LWRES_NETDB_H
+# include <lwres/netdb.h>
+#elif !HAVE_GETADDRINFO
+# include "getaddrinfo.h"
+#else
+# include <netdb.h>
+#endif
+#include "libesmtp-private.h"
+#include "api.h"
+
+struct errno_vars
+ {
+ int error;
+ int herror;
+ };
+
+static inline void
+set_error_internal (struct errno_vars *err, int code)
+{
+ err->error = code;
+ err->herror = 0;
+}
+
+static inline void
+set_herror_internal (struct errno_vars *err, int code)
+{
+ err->herror = code;
+ if (err->herror == EAI_SYSTEM)
+ err->error = errno;
+}
+
+/* Map error codes from getaddrinfo to/from those used by libESMTP. RFC
+ 2553 is silent on whether these values are +ve, -ve, how they sort or
+ even whether they are contiguous so the mapping is done with a
+ switch. NB EAI_SYSTEM is *not* mapped. */
+
+static int
+eai_to_libesmtp (int code)
+{
+#define MAP(code) case code: return SMTP_ERR_##code;
+ switch (code)
+ {
+ MAP(EAI_AGAIN)
+ MAP(EAI_FAIL)
+ MAP(EAI_MEMORY)
+#ifdef EAI_ADDRFAMILY /* it seems OSX does not define this */
+ MAP(EAI_ADDRFAMILY)
+#endif
+#ifdef EAI_NODATA /* it seems OSX does not define this */
+ MAP(EAI_NODATA)
+#endif
+ MAP(EAI_FAMILY)
+ MAP(EAI_BADFLAGS)
+ MAP(EAI_NONAME)
+ MAP(EAI_SERVICE)
+ MAP(EAI_SOCKTYPE)
+ default: return SMTP_ERR_INVAL;
+ }
+#undef MAP
+}
+
+static int
+libesmtp_to_eai (int code)
+{
+#define MAP(code) case SMTP_ERR_##code: return code;
+ switch (code)
+ {
+ MAP(EAI_AGAIN)
+ MAP(EAI_FAIL)
+ MAP(EAI_MEMORY)
+#ifdef EAI_ADDRFAMILY
+ MAP(EAI_ADDRFAMILY)
+#endif
+#ifdef EAI_NODATA
+ MAP(EAI_NODATA)
+#endif
+ MAP(EAI_FAMILY)
+ MAP(EAI_BADFLAGS)
+ MAP(EAI_NONAME)
+ MAP(EAI_SERVICE)
+ MAP(EAI_SOCKTYPE)
+ default: return 0;
+ }
+#undef MAP
+}
+
+static inline int
+get_error_internal (struct errno_vars *err)
+{
+ if (err->herror == 0 || err->herror == EAI_SYSTEM)
+ return err->error;
+ return eai_to_libesmtp (err->herror);
+}
+
+#ifndef USE_PTHREADS
+
+static struct errno_vars libesmtp_errno;
+
+void
+set_error (int code)
+{
+ set_error_internal (&libesmtp_errno, code);
+}
+
+void
+set_herror (int code)
+{
+ set_herror_internal (&libesmtp_errno, code);
+}
+
+int
+smtp_errno (void)
+{
+ return get_error_internal (&libesmtp_errno);
+}
+
+#else
+
+#include <pthread.h>
+
+static pthread_key_t libesmtp_errno;
+static pthread_once_t libesmtp_errno_once = PTHREAD_ONCE_INIT;
+
+static void
+errno_destroy (void *value)
+{
+ if (value != NULL)
+ free (value);
+}
+
+static void
+errno_alloc (void)
+{
+ pthread_key_create (&libesmtp_errno, errno_destroy);
+}
+
+static struct errno_vars *
+errno_ptr (void)
+{
+ struct errno_vars *value;
+
+ pthread_once (&libesmtp_errno_once, errno_alloc);
+ value = pthread_getspecific (libesmtp_errno);
+ if (value == NULL)
+ {
+ value = malloc (sizeof (struct errno_vars));
+ /* FIXME: check for NULL malloc */
+ memset (value, 0, sizeof (struct errno_vars));
+ pthread_setspecific (libesmtp_errno, value);
+ }
+ return value;
+}
+
+void
+set_error (int code)
+{
+ struct errno_vars *value = errno_ptr ();
+
+ if (value != NULL)
+ set_error_internal (value, code);
+}
+
+void
+set_herror (int code)
+{
+ struct errno_vars *value = errno_ptr ();
+
+ if (value != NULL)
+ set_herror_internal (value, code);
+}
+
+int
+smtp_errno (void)
+{
+ struct errno_vars *value = errno_ptr ();
+
+ return (value != NULL) ? get_error_internal (value) : ENOMEM;
+}
+
+#endif
+
+/* store the value of errno in libESMTP's error variable. */
+void
+set_errno (int code)
+{
+ set_error (-code);
+}
+
+static const char *libesmtp_errors[] =
+ {
+ "No Error",
+ "",
+ "Nothing to do", /* NOTHING_TO_DO */
+ "SMTP server dropped connection", /* DROPPED_CONNECTION */
+ "Invalid SMTP syntax in server response", /* INVALID_RESPONSE_SYNTAX */
+ "SMTP Status code mismatch on continuation line", /* STATUS_MISMATCH */
+ "Invalid SMTP status code in server response", /* INVALID_RESPONSE_STATUS */
+ "Invalid API function argument", /* INVAL */
+ "Requested SMTP extension not available", /* EXTENSION_NOT_AVAILABLE */
+ /* Next 10 codes handled by gai_strerror() */
+ NULL, /* EAI_ADDRFAMILY */
+ NULL, /* EAI_NODATA */
+ NULL, /* EAI_FAIL */
+ NULL, /* EAI_AGAIN */
+ NULL, /* EAI_MEMORY */
+ NULL, /* EAI_FAMILY */
+ NULL, /* EAI_BADFLAGS */
+ NULL, /* EAI_NONAME */
+ NULL, /* EAI_SERVICE */
+ NULL, /* EAI_SOCKTYPE */
+ "Unterminated server response", /* UNTERMINATED_RESPONSE */
+ "Client error", /* CLIENT_ERROR */
+ };
+
+char *
+smtp_strerror (int error, char buf[], size_t buflen)
+{
+ const char *text;
+ int len;
+ int map;
+
+ SMTPAPI_CHECK_ARGS (buf != NULL && buflen > 0, NULL);
+
+ if (error < 0)
+#if HAVE_WORKING_STRERROR_R
+ return strerror_r (-error, buf, buflen);
+#elif HAVE_STRERROR_R
+ {
+ /* Assume the broken OSF1 strerror_r which returns an int. */
+ int n = strerror_r (-error, buf, buflen);
+
+ return n >= 0 ? buf : NULL;
+ }
+#else
+ /* Could end up here when threading is enabled but a working
+ strerror_r() is not found. There will be a critical section
+ of code until the returned string is copied to the supplied
+ buffer. This could be solved using a mutex but its hardly
+ worth it since even if libESMTP is protected against itself
+ the application could still call strerror anyway. */
+ text = strerror (-error);
+#endif
+ else if ((map = libesmtp_to_eai (error)) != 0)
+ text = gai_strerror (map);
+ else if (error < (int) (sizeof libesmtp_errors / sizeof libesmtp_errors[0]))
+ text = libesmtp_errors[error];
+ else
+ text = (const char *) 0;
+
+ if (text == (const char *) 0)
+ len = snprintf (buf, buflen, "Error %d", error);
+ else
+ {
+ len = strlen (text);
+ if (len > (int) buflen - 1)
+ len = buflen - 1;
+ if (len > 0)
+ memcpy (buf, text, len);
+ buf[len] = '\0';
+ }
+ return len >= 0 ? buf : NULL;
+}
diff --git a/examples/Makefile b/examples/Makefile
new file mode 100644
index 0000000..70820ed
--- /dev/null
+++ b/examples/Makefile
@@ -0,0 +1,15 @@
+
+OBJECTS = mail-file.o
+LIBESMTP = `libesmtp-config --libs`
+CFLAGS := $(CFLAGS) -std=c99 -pedantic -O2 -g -W -Wall `libesmtp-config --cflags`
+
+all: mail-file-a mail-file-so
+
+mail-file-a: $(OBJECTS)
+ $(CC) -g -static $(OBJECTS) $(LIBESMTP) -o mail-file-a
+
+mail-file-so: $(OBJECTS)
+ $(CC) -g $(OBJECTS) $(LIBESMTP) -o mail-file-so
+
+clean:
+ rm -f *.o core mail-file-a mail-file-so
diff --git a/examples/mail-file.c b/examples/mail-file.c
new file mode 100644
index 0000000..771ce5a
--- /dev/null
+++ b/examples/mail-file.c
@@ -0,0 +1,567 @@
+/*
+ * A libESMTP Example Application.
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* This program accepts a single file argument followed by a list of
+ recipients. The file is mailed to each of the recipients.
+
+ Error checking is minimal to non-existent, this is just a quick
+ and dirty program to give a feel for using libESMTP.
+ */
+#define _XOPEN_SOURCE 500
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <string.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <errno.h>
+#include <stdarg.h>
+
+#include <openssl/ssl.h>
+#include <auth-client.h>
+#include <libesmtp.h>
+
+#if !defined (__GNUC__) || __GNUC__ < 2
+# define __attribute__(x)
+#endif
+#define unused __attribute__((unused))
+
+struct option longopts[] =
+ {
+ { "help", no_argument, NULL, '?', },
+ { "version", no_argument, NULL, 'v', },
+ { "host", required_argument, NULL, 'h', },
+ { "monitor", no_argument, NULL, 'm', },
+ { "crlf", no_argument, NULL, 'c', },
+ { "notify", required_argument, NULL, 'n', },
+ { "mdn", no_argument, NULL, 'd', },
+ { "subject", required_argument, NULL, 's', },
+ { "reverse-path", required_argument, NULL, 'f', },
+ { "tls", no_argument, NULL, 't', },
+ { "require-tls", no_argument, NULL, 'T', },
+ { "noauth", no_argument, NULL, 1, },
+ { NULL, 0, NULL, 0, },
+ };
+
+const char *readlinefp_cb (void **buf, int *len, void *arg);
+void monitor_cb (const char *buf, int buflen, int writing, void *arg);
+void print_recipient_status (smtp_recipient_t recipient,
+ const char *mailbox, void *arg);
+int authinteract (auth_client_request_t request, char **result, int fields,
+ void *arg);
+int tlsinteract (char *buf, int buflen, int rwflag, void *arg);
+ void event_cb (smtp_session_t session, int event_no, void *arg, ...);
+void usage (void);
+void version (void);
+
+int
+main (int argc, char **argv)
+{
+ smtp_session_t session;
+ smtp_message_t message;
+ smtp_recipient_t recipient;
+ auth_context_t authctx;
+ const smtp_status_t *status;
+ struct sigaction sa;
+ char *host = NULL;
+ char *from = NULL;
+ char *subject = NULL;
+ int nocrlf = 0;
+ int noauth = 0;
+ char *file;
+ FILE *fp;
+ int c;
+ enum notify_flags notify = Notify_NOTSET;
+
+ /* This program sends only one message at a time. Create an SMTP
+ session and add a message to it. */
+ auth_client_init ();
+ session = smtp_create_session ();
+ message = smtp_add_message (session);
+
+ while ((c = getopt_long (argc, argv, "dmch:f:s:n:tTv",
+ longopts, NULL)) != EOF)
+ switch (c)
+ {
+ case 'h':
+ host = optarg;
+ break;
+
+ case 'f':
+ from = optarg;
+ break;
+
+ case 's':
+ subject = optarg;
+ break;
+
+ case 'c':
+ nocrlf = 1;
+ break;
+
+ case 'm':
+ smtp_set_monitorcb (session, monitor_cb, stdout, 1);
+ break;
+
+ case 'n':
+ if (strcmp (optarg, "success") == 0)
+ notify |= Notify_SUCCESS;
+ else if (strcmp (optarg, "failure") == 0)
+ notify |= Notify_FAILURE;
+ else if (strcmp (optarg, "delay") == 0)
+ notify |= Notify_DELAY;
+ else if (strcmp (optarg, "never") == 0)
+ notify = Notify_NEVER;
+ break;
+
+ case 'd':
+ /* Request MDN sent to the same address as the reverse path */
+ smtp_set_header (message, "Disposition-Notification-To", NULL, NULL);
+ break;
+
+ case 't':
+ smtp_starttls_enable (session, Starttls_ENABLED);
+ break;
+
+ case 'T':
+ smtp_starttls_enable (session, Starttls_REQUIRED);
+ break;
+
+ case 'v':
+ version ();
+ exit (2);
+
+ case 1:
+ noauth = 1;
+ break;
+
+ default:
+ usage ();
+ exit (2);
+ }
+
+ /* At least two more arguments are needed.
+ */
+ if (optind > argc - 2)
+ {
+ usage ();
+ exit (2);
+ }
+
+ /* NB. libESMTP sets timeouts as it progresses through the protocol.
+ In addition the remote server might close its socket on a timeout.
+ Consequently libESMTP may sometimes try to write to a socket with
+ no reader. Ignore SIGPIPE, then the program doesn't get killed
+ if/when this happens. */
+ sa.sa_handler = SIG_IGN;
+ sigemptyset (&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction (SIGPIPE, &sa, NULL);
+
+ /* Set the host running the SMTP server. LibESMTP has a default port
+ number of 587, however this is not widely deployed so the port
+ is specified as 25 along with the default MTA host. */
+ smtp_set_server (session, host ? host : "localhost:25");
+
+ /* Do what's needed at application level to use authentication.
+ */
+ authctx = auth_create_context ();
+ auth_set_mechanism_flags (authctx, AUTH_PLUGIN_PLAIN, 0);
+ auth_set_interact_cb (authctx, authinteract, NULL);
+
+ /* Use our callback for X.509 certificate passwords. If STARTTLS is
+ not in use or disabled in configure, the following is harmless. */
+ smtp_starttls_set_password_cb (tlsinteract, NULL);
+ smtp_set_eventcb(session, event_cb, NULL);
+
+ /* Now tell libESMTP it can use the SMTP AUTH extension.
+ */
+ if (!noauth)
+ smtp_auth_set_context (session, authctx);
+
+ /* Set the reverse path for the mail envelope. (NULL is ok)
+ */
+ smtp_set_reverse_path (message, from);
+
+#if 0
+ /* The message-id is OPTIONAL but SHOULD be present. By default
+ libESMTP supplies one. If this is not desirable, the following
+ prevents one making its way to the server.
+ N.B. It is not possible to prohibit REQUIRED headers. Furthermore,
+ the submission server will probably add a Message-ID header,
+ so this cannot prevent the delivered message from containing
+ the message-id. */
+ smtp_set_header_option (message, "Message-Id", Hdr_PROHIBIT, 1);
+#endif
+
+ /* RFC 2822 doesn't require recipient headers but a To: header would
+ be nice to have if not present. */
+ smtp_set_header (message, "To", NULL, NULL);
+
+ /* Set the Subject: header. For no reason, we want the supplied subject
+ to override any subject line in the message headers. */
+ if (subject != NULL)
+ {
+ smtp_set_header (message, "Subject", subject);
+ smtp_set_header_option (message, "Subject", Hdr_OVERRIDE, 1);
+ }
+
+ /* Open the message file and set the callback to read it.
+ */
+ file = argv[optind++];
+ if (strcmp (file, "-") == 0)
+ fp = stdin;
+ else if ((fp = fopen (file, "r")) == NULL)
+ {
+ fprintf (stderr, "can't open %s: %s\n", file, strerror (errno));
+ exit (1);
+ }
+ if (nocrlf)
+ smtp_set_messagecb (message, readlinefp_cb, fp);
+ else
+ smtp_set_message_fp (message, fp);
+
+ /* Add remaining program arguments as message recipients.
+ */
+ while (optind < argc)
+ {
+ recipient = smtp_add_recipient (message, argv[optind++]);
+
+ /* Recipient options set here */
+ if (notify != Notify_NOTSET)
+ smtp_dsn_set_notify (recipient, notify);
+ }
+
+ /* Initiate a connection to the SMTP server and transfer the
+ message. */
+ if (!smtp_start_session (session))
+ {
+ char buf[128];
+
+ fprintf (stderr, "SMTP server problem %s\n",
+ smtp_strerror (smtp_errno (), buf, sizeof buf));
+ }
+ else
+ {
+ /* Report on the success or otherwise of the mail transfer.
+ */
+ status = smtp_message_transfer_status (message);
+ printf ("%d %s", status->code,
+ (status->text != NULL) ? status->text : "\n");
+ smtp_enumerate_recipients (message, print_recipient_status, NULL);
+ }
+
+ /* Free resources consumed by the program.
+ */
+ smtp_destroy_session (session);
+ auth_destroy_context (authctx);
+ fclose (fp);
+ auth_client_exit ();
+ exit (0);
+}
+
+/* Callback to prnt the recipient status */
+void
+print_recipient_status (smtp_recipient_t recipient,
+ const char *mailbox, void *arg unused)
+{
+ const smtp_status_t *status;
+
+ status = smtp_recipient_status (recipient);
+ printf ("%s: %d %s", mailbox, status->code, status->text);
+}
+
+/* Callback function to read the message from a file. Since libESMTP
+ does not provide callbacks which translate line endings, one must
+ be provided by the application.
+
+ The message is read a line at a time and the newlines converted
+ to \r\n. Unfortunately, RFC 822 states that bare \n and \r are
+ acceptable in messages and that individually they do not constitute a
+ line termination. This requirement cannot be reconciled with storing
+ messages with Unix line terminations. RFC 2822 rescues this situation
+ slightly by prohibiting lone \r and \n in messages.
+
+ The following code cannot therefore work correctly in all situations.
+ Furthermore it is very inefficient since it must search for the \n.
+ */
+#define BUFLEN 8192
+
+const char *
+readlinefp_cb (void **buf, int *len, void *arg)
+{
+ int octets;
+
+ if (*buf == NULL)
+ *buf = malloc (BUFLEN);
+
+ if (len == NULL)
+ {
+ rewind ((FILE *) arg);
+ return NULL;
+ }
+
+ if (fgets (*buf, BUFLEN - 2, (FILE *) arg) == NULL)
+ octets = 0;
+ else
+ {
+ char *p = strchr (*buf, '\0');
+
+ if (p[-1] == '\n' && p[-2] != '\r')
+ {
+ strcpy (p - 1, "\r\n");
+ p++;
+ }
+ octets = p - (char *) *buf;
+ }
+ *len = octets;
+ return *buf;
+}
+
+void
+monitor_cb (const char *buf, int buflen, int writing, void *arg)
+{
+ FILE *fp = arg;
+
+ if (writing == SMTP_CB_HEADERS)
+ {
+ fputs ("H: ", fp);
+ fwrite (buf, 1, buflen, fp);
+ return;
+ }
+
+ fputs (writing ? "C: " : "S: ", fp);
+ fwrite (buf, 1, buflen, fp);
+ if (buf[buflen - 1] != '\n')
+ putc ('\n', fp);
+}
+
+/* Callback to request user/password info. Not thread safe. */
+int
+authinteract (auth_client_request_t request, char **result, int fields,
+ void *arg unused)
+{
+ char prompt[64];
+ static char resp[512];
+ char *p, *rp;
+ int i, n, tty;
+
+ rp = resp;
+ for (i = 0; i < fields; i++)
+ {
+ n = snprintf (prompt, sizeof prompt, "%s%s: ", request[i].prompt,
+ (request[i].flags & AUTH_CLEARTEXT) ? " (not encrypted)"
+ : "");
+ if (request[i].flags & AUTH_PASS)
+ result[i] = getpass (prompt);
+ else
+ {
+ tty = open ("/dev/tty", O_RDWR);
+ write (tty, prompt, n);
+ n = read (tty, rp, sizeof resp - (rp - resp));
+ close (tty);
+ p = rp + n;
+ while (isspace (p[-1]))
+ p--;
+ *p++ = '\0';
+ result[i] = rp;
+ rp = p;
+ }
+ }
+ return 1;
+}
+
+int
+tlsinteract (char *buf, int buflen, int rwflag unused, void *arg unused)
+{
+ char *pw;
+ int len;
+
+ pw = getpass ("certificate password");
+ len = strlen (pw);
+ if (len + 1 > buflen)
+ return 0;
+ strcpy (buf, pw);
+ return len;
+}
+int
+handle_invalid_peer_certificate(long vfy_result)
+{
+ const char *k ="rare error";
+ switch(vfy_result) {
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+ k="X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT"; break;
+ case X509_V_ERR_UNABLE_TO_GET_CRL:
+ k="X509_V_ERR_UNABLE_TO_GET_CRL"; break;
+ case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
+ k="X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE"; break;
+ case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
+ k="X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE"; break;
+ case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
+ k="X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY"; break;
+ case X509_V_ERR_CERT_SIGNATURE_FAILURE:
+ k="X509_V_ERR_CERT_SIGNATURE_FAILURE"; break;
+ case X509_V_ERR_CRL_SIGNATURE_FAILURE:
+ k="X509_V_ERR_CRL_SIGNATURE_FAILURE"; break;
+ case X509_V_ERR_CERT_NOT_YET_VALID:
+ k="X509_V_ERR_CERT_NOT_YET_VALID"; break;
+ case X509_V_ERR_CERT_HAS_EXPIRED:
+ k="X509_V_ERR_CERT_HAS_EXPIRED"; break;
+ case X509_V_ERR_CRL_NOT_YET_VALID:
+ k="X509_V_ERR_CRL_NOT_YET_VALID"; break;
+ case X509_V_ERR_CRL_HAS_EXPIRED:
+ k="X509_V_ERR_CRL_HAS_EXPIRED"; break;
+ case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+ k="X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD"; break;
+ case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+ k="X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD"; break;
+ case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
+ k="X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD"; break;
+ case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
+ k="X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD"; break;
+ case X509_V_ERR_OUT_OF_MEM:
+ k="X509_V_ERR_OUT_OF_MEM"; break;
+ case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+ k="X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT"; break;
+ case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+ k="X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN"; break;
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+ k="X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY"; break;
+ case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
+ k="X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE"; break;
+ case X509_V_ERR_CERT_CHAIN_TOO_LONG:
+ k="X509_V_ERR_CERT_CHAIN_TOO_LONG"; break;
+ case X509_V_ERR_CERT_REVOKED:
+ k="X509_V_ERR_CERT_REVOKED"; break;
+ case X509_V_ERR_INVALID_CA:
+ k="X509_V_ERR_INVALID_CA"; break;
+ case X509_V_ERR_PATH_LENGTH_EXCEEDED:
+ k="X509_V_ERR_PATH_LENGTH_EXCEEDED"; break;
+ case X509_V_ERR_INVALID_PURPOSE:
+ k="X509_V_ERR_INVALID_PURPOSE"; break;
+ case X509_V_ERR_CERT_UNTRUSTED:
+ k="X509_V_ERR_CERT_UNTRUSTED"; break;
+ case X509_V_ERR_CERT_REJECTED:
+ k="X509_V_ERR_CERT_REJECTED"; break;
+ }
+ printf("SMTP_EV_INVALID_PEER_CERTIFICATE: %ld: %s\n", vfy_result, k);
+ return 1; /* Accept the problem */
+}
+
+void event_cb (smtp_session_t session, int event_no, void *arg,...)
+{
+ va_list alist;
+ int *ok;
+
+ va_start(alist, arg);
+ switch(event_no) {
+ case SMTP_EV_CONNECT:
+ case SMTP_EV_MAILSTATUS:
+ case SMTP_EV_RCPTSTATUS:
+ case SMTP_EV_MESSAGEDATA:
+ case SMTP_EV_MESSAGESENT:
+ case SMTP_EV_DISCONNECT: break;
+ case SMTP_EV_WEAK_CIPHER: {
+ int bits;
+ bits = va_arg(alist, long); ok = va_arg(alist, int*);
+ printf("SMTP_EV_WEAK_CIPHER, bits=%d - accepted.\n", bits);
+ *ok = 1; break;
+ }
+ case SMTP_EV_STARTTLS_OK:
+ puts("SMTP_EV_STARTTLS_OK - TLS started here."); break;
+ case SMTP_EV_INVALID_PEER_CERTIFICATE: {
+ long vfy_result;
+ vfy_result = va_arg(alist, long); ok = va_arg(alist, int*);
+ *ok = handle_invalid_peer_certificate(vfy_result);
+ break;
+ }
+ case SMTP_EV_NO_PEER_CERTIFICATE: {
+ ok = va_arg(alist, int*);
+ puts("SMTP_EV_NO_PEER_CERTIFICATE - accepted.");
+ *ok = 1; break;
+ }
+ case SMTP_EV_WRONG_PEER_CERTIFICATE: {
+ ok = va_arg(alist, int*);
+ puts("SMTP_EV_WRONG_PEER_CERTIFICATE - accepted.");
+ *ok = 1; break;
+ }
+ case SMTP_EV_NO_CLIENT_CERTIFICATE: {
+ ok = va_arg(alist, int*);
+ puts("SMTP_EV_NO_CLIENT_CERTIFICATE - accepted.");
+ *ok = 1; break;
+ }
+ default:
+ printf("Got event: %d - ignored.\n", event_no);
+ }
+ va_end(alist);
+}
+
+void
+usage (void)
+{
+ fputs ("Copyright (C) 2001 Brian Stafford <brian@stafford.uklinux.net>\n"
+ "\n"
+ "This program is free software; you can redistribute it and/or modify\n"
+ "it under the terms of the GNU General Public License as published\n"
+ "by the Free Software Foundation; either version 2 of the License,\n"
+ "or (at your option) any later version.\n"
+ "\n"
+ "This program is distributed in the hope that it will be useful,\n"
+ "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+ "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
+ "GNU General Public License for more details.\n"
+ "\n"
+ "You should have received a copy of the GNU General Public License\n"
+ "along with this program; if not, write to the Free Software\n"
+ "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n"
+ "\n"
+ "usage: mail-file [options] file mailbox [mailbox ...]\n"
+ "\t-h,--host=hostname[:service] -- set SMTP host and service (port)\n"
+ "\t-f,--reverse-path=mailbox -- set reverse path\n"
+ "\t-s,--subject=text -- set subject of the message\n"
+ "\t-n,--notify=success|failure|delay|never -- request DSN\n"
+ "\t-d,--mdn -- request MDN\n"
+ "\t-m,--monitor -- watch the protocol session with the server\n"
+ "\t-c,--crlf -- translate line endings from \\n to CR-LF\n"
+ "\t-t,--tls -- use STARTTLS extension if possible\n"
+ "\t-T,--require-tls -- require use of STARTTLS extension\n"
+ "\t --noauth -- do not attempt to authenticate to the MSA\n"
+ "\t--version -- show version info and exit\n"
+ "\t--help -- this message\n"
+ "\n"
+ "Specify the file argument as \"-\" to read standard input.\n"
+ "The input must be in RFC 2822 format, that is, it must consist\n"
+ "of a sequence of message headers terminated by a blank line and\n"
+ "followed by the message body. Lines must be terminated with the\n"
+ "canonic CR-LF sequence unless the --crlf flag is specified.\n"
+ "Total line length must not exceed 1000 characters.\n",
+ stderr);
+}
+
+void
+version (void)
+{
+ char buf[32];
+
+ smtp_version (buf, sizeof buf, 0);
+ printf ("libESMTP version %s\n", buf);
+}
diff --git a/examples/test-mail b/examples/test-mail
new file mode 100644
index 0000000..4fe8d7c
--- /dev/null
+++ b/examples/test-mail
@@ -0,0 +1,15 @@
+Return-Path: <brian.stafford@ukonline.co.uk>
+Subject: LibESMTP test mail
+MIME-Version: 1.0
+Message-Id: <xyzzy@plugh.org>
+Content-Type: text/plain;
+ charset=iso-8859-1
+Content-Transfer-Encoding: 7bit
+
+Hello world!
+..
+.
+..This is a test
+.More lines
+
+End of test---
diff --git a/getaddrinfo.c b/getaddrinfo.c
new file mode 100644
index 0000000..575b51d
--- /dev/null
+++ b/getaddrinfo.c
@@ -0,0 +1,314 @@
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* An emulation of the RFC 2553 / Posix getaddrinfo resolver interface.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* Need to turn off Posix features in glibc to build this */
+#undef _POSIX_C_SOURCE
+#undef _XOPEN_SOURCE
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <netdb.h>
+
+#include "gethostbyname.h"
+#include "getaddrinfo.h"
+
+static struct addrinfo *
+dup_addrinfo (struct addrinfo *info, void *addr, size_t addrlen)
+{
+ struct addrinfo *ret;
+
+ ret = malloc (sizeof (struct addrinfo));
+ if (ret == NULL)
+ return NULL;
+ memcpy (ret, info, sizeof (struct addrinfo));
+ ret->ai_addr = malloc (addrlen);
+ if (ret->ai_addr == NULL)
+ {
+ free (ret);
+ return NULL;
+ }
+ memcpy (ret->ai_addr, addr, addrlen);
+ ret->ai_addrlen = addrlen;
+ return ret;
+}
+
+int
+getaddrinfo (const char *nodename, const char *servname,
+ const struct addrinfo *hints, struct addrinfo **res)
+{
+ struct hostent *hp;
+ struct servent *servent;
+ const char *socktype;
+ int port;
+ struct addrinfo hint, result;
+ struct addrinfo *ai, *sai, *eai;
+ struct ghbnctx ghbnctx;
+ char **addrs;
+ int code;
+
+ memset (&result, 0, sizeof result);
+
+ /* default for hints */
+ if (hints == NULL)
+ {
+ memset (&hint, 0, sizeof hint);
+ hint.ai_family = PF_UNSPEC;
+ hints = &hint;
+ }
+
+ /* servname must not be NULL in this implementation */
+ if (servname == NULL)
+ return EAI_NONAME;
+
+ /* check for tcp or udp sockets only */
+ if (hints->ai_socktype == SOCK_STREAM)
+ socktype = "tcp";
+ else if (hints->ai_socktype == SOCK_DGRAM)
+ socktype = "udp";
+ else
+ return EAI_SERVICE;
+ result.ai_socktype = hints->ai_socktype;
+
+ /* Note: maintain port in host byte order to make debugging easier */
+ if (isdigit (*servname))
+ port = strtol (servname, NULL, 10);
+ else if ((servent = getservbyname (servname, socktype)) != NULL)
+ port = ntohs (servent->s_port);
+ else
+ return EAI_NONAME;
+
+ /* if nodename == NULL refer to the local host for a client or any
+ for a server */
+ if (nodename == NULL)
+ {
+ struct sockaddr_in sin;
+
+ /* check protocol family is PF_UNSPEC or PF_INET - could try harder
+ for IPv6 but that's more code than I'm prepared to write */
+ if (hints->ai_family == PF_UNSPEC || hints->ai_family == PF_INET)
+ result.ai_family = AF_INET;
+ else
+ return EAI_FAMILY;
+
+ sin.sin_family = result.ai_family;
+ sin.sin_port = htons (port);
+ if (hints->ai_flags & AI_PASSIVE)
+ sin.sin_addr.s_addr = htonl (INADDR_ANY);
+ else
+ sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+ /* Duplicate result and addr and return */
+ *res = dup_addrinfo (&result, &sin, sizeof sin);
+ return (*res == NULL) ? EAI_MEMORY : 0;
+ }
+
+ /* If AI_NUMERIC is specified, use inet_addr to translate numbers and
+ dots notation. */
+ if (hints->ai_flags & AI_NUMERICHOST)
+ {
+ struct sockaddr_in sin;
+
+ /* check protocol family is PF_UNSPEC or PF_INET */
+ if (hints->ai_family == PF_UNSPEC || hints->ai_family == PF_INET)
+ result.ai_family = AF_INET;
+ else
+ return EAI_FAMILY;
+
+ sin.sin_family = result.ai_family;
+ sin.sin_port = htons (port);
+ sin.sin_addr.s_addr = inet_addr (nodename);
+ /* Duplicate result and addr and return */
+ *res = dup_addrinfo (&result, &sin, sizeof sin);
+ return (*res == NULL) ? EAI_MEMORY : 0;
+ }
+
+ errno = 0;
+ hp = gethostbyname_ctx (nodename, &ghbnctx);
+ if (hp == NULL)
+ {
+ if (errno != 0)
+ {
+ free_ghbnctx (&ghbnctx);
+ return EAI_SYSTEM;
+ }
+ code = h_error_ctx (&ghbnctx);
+ switch (code)
+ {
+ case HOST_NOT_FOUND: code = EAI_NODATA; break;
+ case NO_DATA: code = EAI_NODATA; break;
+#if defined(NO_ADDRESS) && NO_ADDRESS != NO_DATA
+ case NO_ADDRESS: code = EAI_NODATA; break;
+#endif
+ case NO_RECOVERY: code = EAI_FAIL; break;
+ case TRY_AGAIN: code = EAI_AGAIN; break;
+ default: code = EAI_FAIL; break;
+ }
+ free_ghbnctx (&ghbnctx);
+ return code;
+ }
+
+ /* Check that the address family is acceptable.
+ */
+ switch (hp->h_addrtype)
+ {
+ case AF_INET:
+ if (!(hints->ai_family == PF_UNSPEC || hints->ai_family == PF_INET))
+ goto eai_family;
+ break;
+#ifdef USE_IPV6
+ case AF_INET6:
+ if (!(hints->ai_family == PF_UNSPEC || hints->ai_family == PF_INET6))
+ goto eai_family;
+ break;
+#endif
+ default:
+ eai_family:
+ free_ghbnctx (&ghbnctx);
+ return EAI_FAMILY;
+ }
+
+ /* For each element pointed to by hp, create an element in the
+ result linked list. */
+ sai = eai = NULL;
+ for (addrs = hp->h_addr_list; *addrs != NULL; addrs++)
+ {
+ struct sockaddr sa;
+ size_t addrlen;
+
+ if (hp->h_length < 1)
+ continue;
+ sa.sa_family = hp->h_addrtype;
+ switch (hp->h_addrtype)
+ {
+ case AF_INET:
+ ((struct sockaddr_in *) &sa)->sin_port = htons (port);
+ memcpy (&((struct sockaddr_in *) &sa)->sin_addr,
+ *addrs, hp->h_length);
+ addrlen = sizeof (struct sockaddr_in);
+ break;
+#ifdef USE_IPV6
+ case AF_INET6:
+# if SIN6_LEN
+ ((struct sockaddr_in6 *) &sa)->sin6_len = hp->h_length;
+# endif
+ ((struct sockaddr_in6 *) &sa)->sin6_port = htons (port);
+ memcpy (&((struct sockaddr_in6 *) &sa)->sin6_addr,
+ *addrs, hp->h_length);
+ addrlen = sizeof (struct sockaddr_in6);
+ break;
+#endif
+ default:
+ continue;
+ }
+
+ result.ai_family = hp->h_addrtype;
+ ai = dup_addrinfo (&result, &sa, addrlen);
+ if (ai == NULL)
+ {
+ free_ghbnctx (&ghbnctx);
+ freeaddrinfo (sai);
+ return EAI_MEMORY;
+ }
+ if (sai == NULL)
+ sai = ai;
+ else
+ eai->ai_next = ai;
+ eai = ai;
+ }
+
+ if (sai == NULL)
+ {
+ free_ghbnctx (&ghbnctx);
+ return EAI_NODATA;
+ }
+
+ if (hints->ai_flags & AI_CANONNAME)
+ {
+ sai->ai_canonname = malloc (strlen (hp->h_name) + 1);
+ if (sai->ai_canonname == NULL)
+ {
+ free_ghbnctx (&ghbnctx);
+ freeaddrinfo (sai);
+ return EAI_MEMORY;
+ }
+ strcpy (sai->ai_canonname, hp->h_name);
+ }
+
+ free_ghbnctx (&ghbnctx);
+ *res = sai;
+ return 0;
+}
+
+void
+freeaddrinfo (struct addrinfo *ai)
+{
+ struct addrinfo *next;
+
+ while (ai != NULL)
+ {
+ next = ai->ai_next;
+ if (ai->ai_canonname != NULL)
+ free (ai->ai_canonname);
+ if (ai->ai_addr != NULL)
+ free (ai->ai_addr);
+ free (ai);
+ ai = next;
+ }
+}
+
+const char *
+gai_strerror (int ecode)
+{
+ static const char *eai_descr[] =
+ {
+ "no error",
+ "address family for nodename not supported", /* EAI_ADDRFAMILY */
+ "temporary failure in name resolution", /* EAI_AGAIN */
+ "invalid value for ai_flags", /* EAI_BADFLAGS */
+ "non-recoverable failure in name resolution", /* EAI_FAIL */
+ "ai_family not supported", /* EAI_FAMILY */
+ "memory allocation failure", /* EAI_MEMORY */
+ "no address associated with nodename", /* EAI_NODATA */
+ "nodename nor servname provided, or not known", /* EAI_NONAME */
+ "servname not supported for ai_socktype", /* EAI_SERVICE */
+ "ai_socktype not supported", /* EAI_SOCKTYPE */
+ "system error returned in errno", /* EAI_SYSTEM */
+ };
+
+ if (ecode < 0 || ecode > (int) (sizeof eai_descr/ sizeof eai_descr[0]))
+ return "unknown error";
+ return eai_descr[ecode];
+}
+
diff --git a/getaddrinfo.h b/getaddrinfo.h
new file mode 100644
index 0000000..c474d3b
--- /dev/null
+++ b/getaddrinfo.h
@@ -0,0 +1,67 @@
+#ifndef _getaddrinfo_h
+#define _getaddrinfo_h
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* Structure and prototypes aken from RFC 2553 */
+
+struct addrinfo
+ {
+ int ai_flags; /* AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST */
+ int ai_family; /* PF_xxx */
+ int ai_socktype; /* SOCK_xxx */
+ int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
+ size_t ai_addrlen; /* length of ai_addr */
+ char *ai_canonname; /* canonical name for nodename */
+ struct sockaddr *ai_addr; /* binary address */
+ struct addrinfo *ai_next; /* next structure in linked list */
+ };
+
+/* Supposed to be defined in <netdb.h> */
+#define AI_PASSIVE 1 /* Socket address is intended for `bind'. */
+#define AI_CANONNAME 2 /* Request for canonical name. */
+#define AI_NUMERICHOST 4 /* Don't use name resolution. */
+
+/* Supposed to be defined in <netdb.h> */
+#define EAI_ADDRFAMILY 1 /* address family for nodename not supported */
+#define EAI_AGAIN 2 /* temporary failure in name resolution */
+#define EAI_BADFLAGS 3 /* invalid value for ai_flags */
+#define EAI_FAIL 4 /* non-recoverable failure in name resolution */
+#define EAI_FAMILY 5 /* ai_family not supported */
+#define EAI_MEMORY 6 /* memory allocation failure */
+#define EAI_NODATA 7 /* no address associated with nodename */
+#define EAI_NONAME 8 /* nodename nor servname provided, or not known */
+#define EAI_SERVICE 9 /* servname not supported for ai_socktype */
+#define EAI_SOCKTYPE 10 /* ai_socktype not supported */
+#define EAI_SYSTEM 11 /* system error returned in errno */
+
+/* RFC 2553 / Posix resolver */
+int getaddrinfo (const char *nodename, const char *servname,
+ const struct addrinfo *hints, struct addrinfo **res);
+
+/* Free addrinfo structure and associated storage */
+void freeaddrinfo (struct addrinfo *ai);
+
+/* Convert error return from getaddrinfo() to string */
+const char *gai_strerror (int code);
+
+#endif
diff --git a/gethostbyname.c b/gethostbyname.c
new file mode 100644
index 0000000..d151606
--- /dev/null
+++ b/gethostbyname.c
@@ -0,0 +1,228 @@
+/*
+ * This file is a ghastly hack because nobody can agree on
+ * gethostbyname_r()'s prototype.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _SVID_SOURCE 1 /* Need this to get gethostbyname_r() */
+
+#include <assert.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <errno.h>
+
+#include "gethostbyname.h"
+
+#if HAVE_GETIPNODEBYNAME
+
+void
+free_ghbnctx (struct ghbnctx *ctx)
+{
+ assert (ctx != NULL);
+
+ if (ctx->hostent != NULL)
+ freehostent (ctx->hostent);
+}
+
+struct hostent *
+gethostbyname_ctx (const char *host, struct ghbnctx *ctx)
+{
+ assert (ctx != NULL);
+
+ memset (ctx, 0, sizeof (struct ghbnctx));
+ ctx->hostent = getipnodebyname (host, AF_UNSPEC, AI_ADDRCONFIG, &ctx->h_err);
+ return ctx->hostent;
+}
+
+int
+h_error_ctx (struct ghbnctx *ctx)
+{
+ assert (ctx != NULL);
+
+ return ctx->h_err;
+}
+
+#elif HAVE_GETHOSTBYNAME_R == 6
+
+void
+free_ghbnctx (struct ghbnctx *ctx)
+{
+ assert (ctx != NULL);
+
+ if (ctx->hostbuf != NULL)
+ free (ctx->hostbuf);
+}
+
+struct hostent *
+gethostbyname_ctx (const char *host, struct ghbnctx *ctx)
+{
+ struct hostent *hp;
+ char *tmp;
+ int err;
+
+ assert (ctx != NULL);
+
+ memset (ctx, 0, sizeof (struct ghbnctx));
+ ctx->hostbuf_len = 2048;
+ if ((ctx->hostbuf = malloc (ctx->hostbuf_len)) == NULL)
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+ while ((err = gethostbyname_r (host,
+ &ctx->hostent, ctx->hostbuf, ctx->hostbuf_len,
+ &hp, &ctx->h_err)) == ERANGE)
+ {
+ ctx->hostbuf_len += 1024;
+ if ((tmp = realloc (ctx->hostbuf, ctx->hostbuf_len)) == NULL)
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+ ctx->hostbuf = tmp;
+ }
+ if (err != 0)
+ {
+ errno = err;
+ return NULL;
+ }
+ return hp;
+}
+
+int
+h_error_ctx (struct ghbnctx *ctx)
+{
+ assert (ctx != NULL);
+
+ return ctx->h_err;
+}
+
+#elif HAVE_GETHOSTBYNAME_R == 5
+
+void
+free_ghbnctx (struct ghbnctx *ctx)
+{
+ assert (ctx != NULL);
+
+ if (ctx->hostbuf != NULL)
+ free (ctx->hostbuf);
+}
+
+struct hostent *
+gethostbyname_ctx (const char *host, struct ghbnctx *ctx)
+{
+ struct hostent *hp;
+ char *tmp;
+
+ assert (ctx != NULL);
+
+ memset (ctx, 0, sizeof (struct ghbnctx));
+ ctx->hostbuf_len = 2048;
+ if ((ctx->hostbuf = malloc (ctx->hostbuf_len)) == NULL)
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+ while ((hp = gethostbyname_r (host, &ctx->hostent,
+ ctx->hostbuf, ctx->hostbuf_len,
+ &ctx->h_err)) == NULL && errno == ERANGE)
+ {
+ ctx->hostbuf_len += 1024;
+ if ((tmp = realloc (ctx->hostbuf, ctx->hostbuf_len)) == NULL)
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+ ctx->hostbuf = tmp;
+ }
+ return hp;
+}
+
+int
+h_error_ctx (struct ghbnctx *ctx)
+{
+ assert (ctx != NULL);
+
+ return ctx->h_err;
+}
+
+#elif HAVE_GETHOSTBYNAME_R == 3
+
+void
+free_ghbnctx (struct ghbnctx *ctx)
+{
+ assert (ctx != NULL);
+
+ /* FIXME: does this need to do anything? */
+}
+
+struct hostent *
+gethostbyname_ctx (const char *host, struct ghbnctx *ctx)
+{
+ assert (ctx != NULL);
+
+ if (!gethostbyname_r (host, &ctx->hostent, &ctx->hostent_data))
+ {
+ ctx->h_err = h_errno; /* FIXME: is this correct? */
+ return NULL;
+ }
+ return &ctx->hostent;
+}
+
+int
+h_error_ctx (struct ghbnctx *ctx)
+{
+ assert (ctx != NULL);
+
+ return ctx->h_err;
+}
+
+#else
+
+void
+free_ghbnctx (struct ghbnctx *ctx __attribute__ ((unused)))
+{
+ assert (ctx != NULL);
+}
+
+struct hostent *
+gethostbyname_ctx (const char *host, struct ghbnctx *ctx)
+{
+ struct hostent *hp;
+
+ hp = gethostbyname (host);
+ if (hp == NULL)
+ ctx->h_err = h_errno;
+ return hp;
+}
+
+int
+h_error_ctx (struct ghbnctx *ctx)
+{
+ assert (ctx != NULL);
+
+ return ctx->h_err;
+}
+
+#endif
diff --git a/gethostbyname.h b/gethostbyname.h
new file mode 100644
index 0000000..2b96399
--- /dev/null
+++ b/gethostbyname.h
@@ -0,0 +1,103 @@
+/*
+ * This file is a ghastly hack because nobody can agree on
+ * gethostbyname_r()'s prototype.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*************************************************************************
+ Usage:
+
+ #include <errno.h>
+ #include "gethostbyname.h"
+
+ f ()
+ {
+ struct ghbnctx ctx;
+
+ errno = 0;
+ hp = gethostbyname_ctx (host, &ctx);
+ if (hp == NULL)
+ {
+ if (errno != 0)
+ handle_value_of_errno (errno);
+ else
+ handle_value_of_h_errno (h_error_ctx (&ctx));
+ }
+ else
+ {
+ ...
+ }
+ free_ghbnctx (&ctx);
+ }
+ *************************************************************************/
+
+#ifndef _gethostbyname_h
+#define _gethostbyname_h
+
+#if HAVE_GETIPNODEBYNAME
+
+struct ghbnctx
+ {
+ int h_err;
+ struct hostent *hostent;
+ };
+
+#elif HAVE_GETHOSTBYNAME_R == 6
+
+struct ghbnctx
+ {
+ int h_err;
+ struct hostent hostent;
+ char *hostbuf;
+ size_t hostbuf_len;
+ };
+
+#elif HAVE_GETHOSTBYNAME_R == 5
+
+struct ghbnctx
+ {
+ int h_err;
+ struct hostent hostent;
+ char *hostbuf;
+ int hostbuf_len;
+ };
+
+#elif HAVE_GETHOSTBYNAME_R == 3
+
+struct ghbnctx
+ {
+ int h_err;
+ struct hostent_data hostent_data;
+ struct hostent hostent;
+ };
+
+#else
+
+struct ghbnctx
+ {
+ int h_err;
+ };
+
+#endif
+
+struct hostent *gethostbyname_ctx (const char *host, struct ghbnctx *ctx);
+int h_error_ctx (struct ghbnctx *ctx);
+void free_ghbnctx (struct ghbnctx *ctx);
+
+#endif
+
diff --git a/headers.c b/headers.c
new file mode 100644
index 0000000..abf3bf5
--- /dev/null
+++ b/headers.c
@@ -0,0 +1,906 @@
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+
+#include <missing.h>
+
+#include "libesmtp-private.h"
+#include "headers.h"
+#include "htable.h"
+#include "rfc2822date.h"
+#include "api.h"
+
+struct rfc2822_header
+ {
+ struct rfc2822_header *next;
+ struct header_info *info; /* Info for setting and printing */
+ char *header; /* Header name */
+ void *value; /* Header value */
+ };
+
+typedef int (*hdrset_t) (struct rfc2822_header *, va_list);
+typedef void (*hdrprint_t) (smtp_message_t, struct rfc2822_header *);
+typedef void (*hdrdestroy_t) (struct rfc2822_header *);
+
+struct header_actions
+ {
+ const char *name; /* Header for which the action is specified */
+ unsigned int flags; /* Header flags */
+ hdrset_t set; /* Function to set header value from API */
+ hdrprint_t print; /* Function to print the header value */
+ hdrdestroy_t destroy; /* Function to destroy the header value */
+ };
+
+struct header_info
+ {
+ const struct header_actions *action;
+ struct rfc2822_header *hdr; /* Pointer to most recently defined header */
+ unsigned int seen : 1; /* Header has been seen in the message */
+ unsigned int override : 1; /* LibESMTP is overriding the message */
+ unsigned int prohibit : 1; /* Header may not appear in the message */
+ };
+
+#define NELT(x) ((int) (sizeof x / sizeof x[0]))
+
+#define OPTIONAL 0
+#define SHOULD 1
+#define REQUIRE 2
+#define PROHIBIT 4
+#define PRESERVE 8
+#define LISTVALUE 16
+#define MULTIPLE 32
+
+static struct rfc2822_header *create_header (smtp_message_t message,
+ const char *header,
+ struct header_info *info);
+void destroy_string (struct rfc2822_header *header);
+void destroy_mbox_list (struct rfc2822_header *header);
+struct header_info *find_header (smtp_message_t message,
+ const char *name, int len);
+struct header_info *insert_header (smtp_message_t message, const char *name);
+
+/* RFC 2822 headers processing */
+
+/****************************************************************************
+ * Functions for setting and printing header values
+ ****************************************************************************/
+
+static int
+set_string (struct rfc2822_header *header, va_list alist)
+{
+ const char *value;
+
+ assert (header != NULL);
+
+ if (header->value != NULL) /* Already set */
+ return 0;
+
+ value = va_arg (alist, const char *);
+ if (value == NULL)
+ return 0;
+ header->value = strdup (value);
+ return header->value != NULL;
+}
+
+static int
+set_string_null (struct rfc2822_header *header, va_list alist)
+{
+ const char *value;
+
+ assert (header != NULL);
+
+ if (header->value != NULL) /* Already set */
+ return 0;
+
+ value = va_arg (alist, const char *);
+ if (value == NULL)
+ return 1;
+ header->value = strdup (value);
+ return header->value != NULL;
+}
+
+/* Print header-name ": " header-value "\r\n" */
+static void
+print_string (smtp_message_t message, struct rfc2822_header *header)
+{
+ assert (message != NULL && header != NULL);
+
+ /* TODO: implement line folding at white spaces */
+ vconcatenate (&message->hdr_buffer, header->header, ": ",
+ (header->value != NULL) ? header->value : "", "\r\n", NULL);
+}
+
+void
+destroy_string (struct rfc2822_header *header)
+{
+ assert (header != NULL);
+
+ if (header->value != NULL)
+ free (header->value);
+}
+
+/* Print header-name ": <" message-id ">\r\n" */
+static void
+print_message_id (smtp_message_t message, struct rfc2822_header *header)
+{
+ const char *message_id;
+ char buf[64];
+#ifdef HAVE_GETTIMEOFDAY
+ struct timeval tv;
+#endif
+
+ assert (message != NULL && header != NULL);
+
+ message_id = header->value;
+ if (message_id == NULL)
+ {
+#ifdef HAVE_GETTIMEOFDAY
+ if (gettimeofday (&tv, NULL) == -1) /* This shouldn't fail ... */
+ snprintf (buf, sizeof buf, "%ld.%ld.%d@%s", tv.tv_sec, tv.tv_usec,
+ getpid (), message->session->localhost);
+ else /* ... but if it does fall back to using time() */
+#endif
+ snprintf (buf, sizeof buf, "%ld.%d@%s", time (NULL),
+ getpid (), message->session->localhost);
+ message_id = buf;
+ }
+ /* TODO: implement line folding at white spaces */
+ vconcatenate (&message->hdr_buffer,
+ header->header, ": <", message_id, ">\r\n",
+ NULL);
+}
+
+/****/
+
+static int
+set_date (struct rfc2822_header *header, va_list alist)
+{
+ const time_t *value;
+
+ assert (header != NULL);
+
+ if ((time_t) header->value != (time_t) 0) /* Already set */
+ return 0;
+
+ value = va_arg (alist, const time_t *);
+ header->value = (void *) *value;
+ return 1;
+}
+
+/* Print header-name ": " formatted-date "\r\n" */
+static void
+print_date (smtp_message_t message, struct rfc2822_header *header)
+{
+ char buf[64];
+ time_t when;
+
+ assert (message != NULL && header != NULL);
+
+ when = (time_t) header->value;
+ if (when == (time_t) 0)
+ time (&when);
+ vconcatenate (&message->hdr_buffer, header->header, ": ",
+ rfc2822date (buf, sizeof buf, &when), "\r\n", NULL);
+}
+
+/****/
+
+struct mbox
+ {
+ struct mbox *next;
+ char *mailbox;
+ char *phrase;
+ };
+
+void
+destroy_mbox_list (struct rfc2822_header *header)
+{
+ struct mbox *mbox, *next;
+
+ assert (header != NULL);
+
+ mbox = header->value;
+ while (mbox != NULL)
+ {
+ next = mbox->next;
+ if (mbox->phrase != NULL)
+ free ((void *) mbox->phrase);
+ if (mbox->mailbox != NULL)
+ free ((void *) mbox->mailbox);
+ free (mbox);
+ mbox = next;
+ }
+}
+
+static int
+set_from (struct rfc2822_header *header, va_list alist)
+{
+ struct mbox *mbox;
+ const char *mailbox;
+ const char *phrase;
+
+ assert (header != NULL);
+
+ phrase = va_arg (alist, const char *);
+ mailbox = va_arg (alist, const char *);
+
+ /* Allow this to succeed as a special case. Effectively requesting
+ default action in print_from(). Fails if explicit values have
+ already been set. */
+ if (phrase == NULL && mailbox == NULL)
+ return header->value == NULL;
+
+ mbox = malloc (sizeof (struct mbox));
+ if (mbox == NULL)
+ return 0;
+ mbox->phrase = (phrase != NULL) ? strdup (phrase) : NULL;
+ mbox->mailbox = strdup (mailbox);
+
+ mbox->next = header->value;
+ header->value = mbox;
+ return 1;
+}
+
+/* Print header-name ": " mailbox "\r\n"
+ or header-name ": \"" phrase "\" <" mailbox ">\r\n" */
+static void
+print_from (smtp_message_t message, struct rfc2822_header *header)
+{
+ struct mbox *mbox;
+ const char *mailbox;
+
+ assert (message != NULL && header != NULL);
+
+ vconcatenate (&message->hdr_buffer, header->header, ": ", NULL);
+ /* TODO: implement line folding at white spaces */
+ if (header->value == NULL)
+ {
+ mailbox = message->reverse_path_mailbox;
+ vconcatenate (&message->hdr_buffer,
+ (mailbox != NULL && *mailbox != '\0') ? mailbox : "<>",
+ "\r\n", NULL);
+ }
+ else
+ for (mbox = header->value; mbox != NULL; mbox = mbox->next)
+ {
+ mailbox = mbox->mailbox;
+ if (mbox->phrase == NULL)
+ vconcatenate (&message->hdr_buffer,
+ (mailbox != NULL && *mailbox != '\0') ? mailbox : "<>",
+ NULL);
+ else
+ vconcatenate (&message->hdr_buffer, "\"", mbox->phrase, "\""
+ " <", (mailbox != NULL) ? mailbox : "", ">", NULL);
+ vconcatenate (&message->hdr_buffer,
+ (mbox->next != NULL) ? ",\r\n " : "\r\n", NULL);
+ }
+}
+
+/* Same arguments and syntax as from: except that only one value is
+ allowed. */
+static int
+set_sender (struct rfc2822_header *header, va_list alist)
+{
+ struct mbox *mbox;
+ const char *mailbox;
+ const char *phrase;
+
+ assert (header != NULL);
+
+ if (header->value != NULL)
+ return 0;
+
+ phrase = va_arg (alist, const char *);
+ mailbox = va_arg (alist, const char *);
+ if (phrase == NULL && mailbox == NULL)
+ return 0;
+
+ mbox = malloc (sizeof (struct mbox));
+ if (mbox == NULL)
+ return 0;
+ mbox->phrase = (phrase != NULL) ? strdup (phrase) : NULL;
+ mbox->mailbox = strdup (mailbox);
+ mbox->next = NULL;
+
+ mbox->next = header->value;
+ return 1;
+}
+
+/* TODO: do nothing if the mailbox is NULL. Check this doesn't fool
+ the protocol engine into thinking it has seen end of file. */
+/* Print header-name ": " mailbox "\r\n"
+ or header-name ": \"" phrase "\" <" mailbox ">\r\n"
+ */
+static void
+print_sender (smtp_message_t message, struct rfc2822_header *header)
+{
+ struct mbox *mbox;
+ const char *mailbox;
+
+ assert (message != NULL && header != NULL);
+
+ vconcatenate (&message->hdr_buffer, header->header, ": ", NULL);
+ mbox = header->value;
+ mailbox = mbox->mailbox;
+ if (mbox->phrase == NULL)
+ vconcatenate (&message->hdr_buffer,
+ (mailbox != NULL && *mailbox != '\0') ? mailbox : "<>",
+ "\r\n", NULL);
+ else
+ vconcatenate (&message->hdr_buffer, "\"", mbox->phrase, "\""
+ " <", (mailbox != NULL) ? mailbox : "", ">\r\n", NULL);
+}
+
+static int
+set_to (struct rfc2822_header *header, va_list alist)
+{
+ struct mbox *mbox;
+ const char *mailbox;
+ const char *phrase;
+
+ assert (header != NULL);
+
+ phrase = va_arg (alist, const char *);
+ mailbox = va_arg (alist, const char *);
+ if (phrase == NULL && mailbox == NULL)
+ mbox = NULL;
+ else
+ {
+ mbox = malloc (sizeof (struct mbox));
+ if (mbox == NULL)
+ return 0;
+ mbox->phrase = (phrase != NULL) ? strdup (phrase) : NULL;
+ mbox->mailbox = strdup (mailbox);
+
+ mbox->next = header->value;
+ }
+ header->value = mbox;
+ return 1;
+}
+
+static int
+set_cc (struct rfc2822_header *header, va_list alist)
+{
+ struct mbox *mbox;
+ const char *mailbox;
+ const char *phrase;
+
+ assert (header != NULL);
+
+ phrase = va_arg (alist, const char *);
+ mailbox = va_arg (alist, const char *);
+ if (mailbox == NULL)
+ return 0;
+ mbox = malloc (sizeof (struct mbox));
+ if (mbox == NULL)
+ return 0;
+ mbox->phrase = (phrase != NULL) ? strdup (phrase) : NULL;
+ mbox->mailbox = strdup (mailbox);
+
+ mbox->next = header->value;
+ header->value = mbox;
+ return 1;
+}
+
+/* Print header-name ": " mailbox "\r\n"
+ or header-name ": \"" phrase "\" <" mailbox ">\r\n"
+ ad nauseum. */
+static void
+print_cc (smtp_message_t message, struct rfc2822_header *header)
+{
+ struct mbox *mbox;
+
+ assert (message != NULL && header != NULL);
+
+ vconcatenate (&message->hdr_buffer, header->header, ": ", NULL);
+ for (mbox = header->value; mbox != NULL; mbox = mbox->next)
+ {
+ if (mbox->phrase == NULL)
+ vconcatenate (&message->hdr_buffer, mbox->mailbox, NULL);
+ else
+ vconcatenate (&message->hdr_buffer,
+ "\"", mbox->phrase, "\" <", mbox->mailbox, ">",
+ NULL);
+ vconcatenate (&message->hdr_buffer,
+ (mbox->next != NULL) ? ",\r\n " : "\r\n", NULL);
+ }
+}
+
+/* As above but generate a default value from the recipient list.
+ */
+static void
+print_to (smtp_message_t message, struct rfc2822_header *header)
+{
+ smtp_recipient_t recipient;
+
+ assert (header != NULL);
+
+ if (header->value != NULL)
+ {
+ print_cc (message, header);
+ return;
+ }
+
+ /* TODO: implement line folding at white spaces */
+ vconcatenate (&message->hdr_buffer, header->header, ": ", NULL);
+ for (recipient = message->recipients;
+ recipient != NULL;
+ recipient = recipient->next)
+ vconcatenate (&message->hdr_buffer, recipient->mailbox,
+ (recipient->next != NULL) ? ",\r\n " : "\r\n",
+ NULL);
+}
+
+
+/* Header actions placed here to avoid the need for many akward forward
+ declarations for set_xxx/print_xxx. */
+
+static const struct header_actions header_actions[] =
+ {
+ /* This is the default header info for a simple string value.
+ */
+ { NULL, OPTIONAL,
+ set_string, print_string, destroy_string},
+ /* A number of headers should be present in every message
+ */
+ { "Date", REQUIRE,
+ set_date, print_date, NULL, },
+ { "From", REQUIRE,
+ set_from, print_from, destroy_mbox_list, },
+ /* Certain headers are added when a message is delivered and
+ should not be present in a message being posted or which
+ is in transit. If present in the message they will be stripped
+ and if specified by the API, the relevant APIs will fail. */
+ { "Return-Path", PROHIBIT, NULL, NULL, NULL, },
+ /* RFC 2298 - Delivering MTA may add the Original-Recipient: header
+ using DSN ORCPT parameter and may discard
+ Original-Recipient: headers present in the message.
+ No point in sending it then. */
+ { "Original-Recipient", PROHIBIT, NULL, NULL, NULL, },
+ /* MIME-*: and Content-*: are MIME headers and must not be generated
+ or processed by libESMTP. Similarly, Resent-*: and Received: must
+ be retained unaltered. */
+ { "Content-", PRESERVE, NULL, NULL, NULL, },
+ { "MIME-", PRESERVE, NULL, NULL, NULL, },
+ { "Resent-", PRESERVE, NULL, NULL, NULL, },
+ { "Received", PRESERVE, NULL, NULL, NULL, },
+ /* Headers which are optional but which are recommended to be
+ present. Default action is to provide a default unless the
+ application explicitly requests not to. */
+ { "Message-Id", SHOULD,
+ set_string_null,print_message_id, destroy_string, },
+ /* Remaining headers are known to libESMTP to simplify handling them
+ for the application. All other headers are reaated as simple
+ string values. */
+ { "Sender", OPTIONAL,
+ set_sender, print_sender, destroy_mbox_list, },
+ { "To", OPTIONAL,
+ set_to, print_to, destroy_mbox_list, },
+ { "Cc", OPTIONAL,
+ set_cc, print_cc, destroy_mbox_list, },
+ { "Bcc", OPTIONAL,
+ set_cc, print_cc, destroy_mbox_list, },
+ { "Reply-To", OPTIONAL,
+ set_cc, print_cc, destroy_mbox_list, },
+ /* RFC 2298 - MDN request. Syntax is the same as the From: header and
+ default when set to NULL is the same as From: */
+ { "Disposition-Notification-To", OPTIONAL,
+ set_from, print_from, destroy_mbox_list, },
+ /* TODO:
+ In-Reply-To: *(phrase / msgid)
+ References: *(phrase / msgid)
+ Keywords: #phrase
+
+ Handle Resent- versions of
+ To Cc Bcc Message-ID Date Reply-To From Sender
+ */
+ };
+
+static int
+init_header_table (smtp_message_t message)
+{
+ int i;
+ struct header_info *hi;
+
+ assert (message != NULL);
+
+ if (message->hdr_action != NULL)
+ return -1;
+
+ message->hdr_action = h_create ();
+ if (message->hdr_action == NULL)
+ return 0;
+ for (i = 0; i < NELT (header_actions); i++)
+ if (header_actions[i].name != NULL)
+ {
+ hi = h_insert (message->hdr_action, header_actions[i].name, -1,
+ sizeof (struct header_info));
+ if (hi == NULL)
+ return 0;
+ hi->action = &header_actions[i];
+
+ /* REQUIREd headers must be present in the message. SHOULD
+ means the header is optional but its presence is recommended.
+ Create a NULL valued header. This will either be set later
+ with the API, or the print_xxx function will handle the NULL
+ value as a special case, e.g, the To: header is generated
+ from the recipient_t list. */
+ if (hi->action->flags & (REQUIRE | SHOULD))
+ {
+ if (create_header (message, header_actions[i].name, hi) == NULL)
+ return 0;
+ }
+ }
+ return 1;
+}
+
+void
+destroy_header_table (smtp_message_t message)
+{
+ struct rfc2822_header *header, *next;
+
+ assert (message != NULL);
+
+ /* Take out the linked list */
+ for (header = message->headers; header!= NULL; header = next)
+ {
+ next = header->next;
+ if (header->info->action->destroy != NULL)
+ (*header->info->action->destroy) (header);
+ free (header->header);
+ free (header);
+ }
+
+ /* Take out the hash table */
+ if (message->hdr_action != NULL)
+ {
+ h_destroy (message->hdr_action, NULL, NULL);
+ message->hdr_action = NULL;
+ }
+
+ message->headers = message->end_headers = NULL;
+}
+
+struct header_info *
+find_header (smtp_message_t message, const char *name, int len)
+{
+ struct header_info *info;
+ const char *p;
+
+ assert (message != NULL && name != NULL);
+
+ if (len < 0)
+ len = strlen (name);
+ if (len == 0)
+ return NULL;
+ info = h_search (message->hdr_action, name, len);
+ if (info == NULL && (p = memchr (name, '-', len)) != NULL)
+ info = h_search (message->hdr_action, name, p - name + 1);
+ return info;
+}
+
+struct header_info *
+insert_header (smtp_message_t message, const char *name)
+{
+ struct header_info *info;
+
+ assert (message != NULL && name != NULL);
+
+ info = h_insert (message->hdr_action, name, -1, sizeof (struct header_info));
+ if (info == NULL)
+ return NULL;
+ info->action = &header_actions[0];
+ return info;
+}
+
+static struct rfc2822_header *
+create_header (smtp_message_t message, const char *header,
+ struct header_info *info)
+{
+ struct rfc2822_header *hdr;
+
+ assert (message != NULL && header != NULL && info != NULL);
+
+ if ((hdr = malloc (sizeof (struct rfc2822_header))) == NULL)
+ return NULL;
+
+ memset (hdr, 0, sizeof (struct rfc2822_header));
+ hdr->header = strdup (header);
+ hdr->info = info;
+ info->hdr = hdr;
+ APPEND_LIST (message->headers, message->end_headers, hdr);
+ return hdr;
+}
+
+/****************************************************************************
+ * Header processing
+ ****************************************************************************/
+
+/* Called just before copying the messge from the application.
+ Resets the seen flag for headers libESMTP is interested in */
+
+static void
+reset_headercb (const char *name __attribute__ ((unused)),
+ void *data, void *arg __attribute__ ((unused)))
+{
+ struct header_info *info = data;
+
+ assert (info != NULL);
+
+ info->seen = 0;
+}
+
+int
+reset_header_table (smtp_message_t message)
+{
+ int status;
+
+ assert (message != NULL);
+
+ if ((status = init_header_table (message)) < 0)
+ h_enumerate (message->hdr_action, reset_headercb, NULL);
+ return status;
+}
+
+/* This is called to process headers present in the application supplied
+ message. */
+const char *
+process_header (smtp_message_t message, const char *header, int *len)
+{
+ const char *p;
+ struct header_info *info;
+ const struct header_actions *action;
+ hdrprint_t print;
+
+ assert (message != NULL && header != NULL && len != NULL);
+
+ if (*len > 0
+ && (p = memchr (header, ':', *len)) != NULL
+ && (info = find_header (message, header, p - header)) != NULL)
+ {
+ if ((action = info->action) != NULL)
+ {
+ /* RFC 2822 states that headers may only appear once in a
+ message with the exception of a few special headers.
+ This restriction is enforced here. */
+ if (info->seen && !(action->flags & (MULTIPLE | PRESERVE)))
+ header = NULL;
+ if (info->prohibit || (action->flags & PROHIBIT))
+ header = NULL;
+
+ /* When libESMTP is overriding headers in the message with
+ ones supplied in the API, the substitution is done here
+ to preserve the original ordering of the headers. */
+ if (header != NULL && info->override)
+ {
+ if ((print = action->print) == NULL)
+ print = print_string;
+ cat_reset (&message->hdr_buffer, 0);
+ (*print) (message, info->hdr);
+ header = cat_buffer (&message->hdr_buffer, len);
+ }
+ }
+ else if (info->seen)
+ header = NULL;
+ info->seen = 1;
+ }
+ return header;
+}
+
+/* This is called to supply headers not present in the application supplied
+ message. */
+const char *
+missing_header (smtp_message_t message, int *len)
+{
+ struct header_info *info;
+ hdrprint_t print;
+
+ assert (message != NULL && len != NULL);
+
+ /* Move on to the next header */
+ if (message->current_header == NULL)
+ message->current_header = message->headers;
+ else
+ message->current_header = message->current_header->next;
+
+ /* Look for the next header that is actually required */
+ print = NULL;
+ while (message->current_header != NULL)
+ {
+ info = message->current_header->info;
+ if (info == NULL) /* shouldn't happen */
+ break;
+ if (!info->seen)
+ {
+ if (info->action != NULL)
+ print = info->action->print;
+ break;
+ }
+ message->current_header = message->current_header->next;
+ }
+ if (message->current_header == NULL)
+ {
+ /* Free the buffer created by concatenate() and return NULL to
+ mark the end of the headers */
+ cat_free (&message->hdr_buffer);
+ return NULL;
+ }
+
+ if (print == NULL)
+ print = print_string;
+
+ cat_reset (&message->hdr_buffer, 0);
+ (*print) (message, message->current_header);
+ return cat_buffer (&message->hdr_buffer, len);
+}
+
+/****************************************************************************
+ * Header API
+ ****************************************************************************/
+
+int
+smtp_set_header (smtp_message_t message, const char *header, ...)
+{
+ va_list alist;
+ struct rfc2822_header *hdr;
+ struct header_info *info;
+ hdrset_t set;
+
+ SMTPAPI_CHECK_ARGS (message != NULL && header != NULL, 0);
+
+ if (!init_header_table (message))
+ {
+ set_errno (ENOMEM);
+ return 0;
+ }
+
+ info = find_header (message, header, -1);
+ if (info == NULL && (info = insert_header (message, header)) == NULL)
+ {
+ set_errno (ENOMEM);
+ return 0;
+ }
+
+ /* Cannot specify a value for headers that must pass unchanged (MIME)
+ or which may not appear in a posted message (Return-Path:). */
+ if (info->prohibit || (info->action->flags & (PROHIBIT | PRESERVE)))
+ {
+ set_error (SMTP_ERR_INVAL);
+ return 0;
+ }
+
+ set = info->action->set;
+ if (set == NULL)
+ {
+ set_error (SMTP_ERR_INVAL);
+ return 0;
+ }
+
+ if (info->hdr == NULL)
+ hdr = create_header (message, header, info);
+ else if (info->hdr->value == NULL)
+ hdr = info->hdr;
+ else
+ {
+ /* Header has a previous value. If multiple headers are permitted,
+ create a new value. If the header has a list value, the value
+ is appended to the iost. If neither condition applies, this
+ is an error. */
+ if (info->action->flags & MULTIPLE)
+ hdr = create_header (message, header, info);
+ else if (info->action->flags & LISTVALUE)
+ hdr = info->hdr;
+ else
+ {
+ set_error (SMTP_ERR_INVAL);
+ return 0;
+ }
+ }
+
+ /* Set its value */
+ va_start (alist, header);
+ (*set) (hdr, alist);
+ va_end (alist);
+
+ return 1;
+}
+
+int
+smtp_set_header_option (smtp_message_t message, const char *header,
+ enum header_option option, ...)
+{
+ va_list alist;
+ struct header_info *info;
+
+ SMTPAPI_CHECK_ARGS (message != NULL && header != NULL, 0);
+
+ if (!init_header_table (message))
+ {
+ set_errno (ENOMEM);
+ return 0;
+ }
+
+ info = find_header (message, header, -1);
+ if (info == NULL && (info = insert_header (message, header)) == NULL)
+ {
+ set_errno (ENOMEM);
+ return 0;
+ }
+
+ /* Don't permit options to be set on headers that must pass intact or
+ which are prohibited. */
+ if (info->action->flags & (PROHIBIT | PRESERVE))
+ {
+ set_error (SMTP_ERR_INVAL);
+ return 0;
+ }
+
+ /* There is an odd quirk when setting options. Setting an option for
+ the OPTIONAL headers known to libESMTP causes default values to be
+ generated automatically when not found in the message, so long as
+ there is no other reason to prevent them appearing in the message! */
+
+ /* Don't allow the user to set override on prohibited headers. */
+ if (option == Hdr_OVERRIDE && !info->prohibit)
+ {
+ va_start (alist, option);
+ info->override = !!va_arg (alist, int);
+ va_end (alist);
+ return 1;
+ }
+ /* Don't allow the user to prohibit required headers. */
+ if (option == Hdr_PROHIBIT && !(info->action->flags & REQUIRE))
+ {
+ va_start (alist, option);
+ info->prohibit = !!va_arg (alist, int);
+ va_end (alist);
+ return 1;
+ }
+
+ set_error (SMTP_ERR_INVAL);
+ return 0;
+}
+
+int
+smtp_set_resent_headers (smtp_message_t message, int onoff)
+{
+ SMTPAPI_CHECK_ARGS (message != NULL, 0);
+
+ /* TODO: place holder, implement real functionality here.
+ For now, succeed if the onoff argument is zero. */
+ SMTPAPI_CHECK_ARGS (onoff == 0, 0);
+
+ return 1;
+}
diff --git a/headers.h b/headers.h
new file mode 100644
index 0000000..9d96c76
--- /dev/null
+++ b/headers.h
@@ -0,0 +1,32 @@
+#ifndef _headers_h
+#define _headers_h
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+int reset_header_table (smtp_message_t message);
+const char *process_header (smtp_message_t message,
+ const char *header, int *len);
+const char *missing_header (smtp_message_t message, int *len);
+void destroy_header_table (smtp_message_t message);
+
+#endif
diff --git a/htable.c b/htable.c
new file mode 100644
index 0000000..7277ada
--- /dev/null
+++ b/htable.c
@@ -0,0 +1,228 @@
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+
+/* A simplistic hash table implementation using hashing and chaining.
+ The table is always 256 entries long although this is an arbitrary
+ choice and there is nothing in the code to prevent other table sizes
+ or hash functions from working.
+
+ Case insensitive searching is performed.
+*/
+
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+#include <missing.h> /* declarations for missing library functions */
+
+#include "htable.h"
+
+struct h_node
+ {
+ struct h_node *next; /* Next node in chain for this hash value */
+ char *name; /* Node name */
+ };
+
+#define HASHSIZE 256
+
+static const unsigned char shuffle[HASHSIZE] =
+ {
+ 215, 207, 188, 72, 82, 194, 89, 230,
+ 17, 49, 127, 179, 139, 200, 104, 114,
+ 233, 52, 138, 42, 175, 159, 142, 77,
+ 247, 3, 185, 54, 157, 19, 153, 14,
+ 112, 184, 32, 220, 20, 148, 251, 141,
+ 66, 195, 174, 150, 246, 76, 242, 227,
+ 145, 84, 7, 5, 144, 211, 31, 71,
+ 123, 217, 134, 243, 152, 137, 67, 213,
+ 83, 223, 203, 119, 110, 113, 99, 158,
+ 156, 61, 85, 187, 151, 90, 6, 237,
+ 177, 45, 133, 87, 27, 106, 15, 68,
+ 50, 80, 239, 250, 108, 253, 199, 124,
+ 2, 210, 205, 21, 209, 252, 29, 196,
+ 219, 78, 86, 178, 22, 53, 74, 9,
+ 155, 91, 122, 235, 65, 129, 64, 206,
+ 41, 46, 245, 125, 198, 189, 94, 79,
+ 101, 160, 193, 43, 216, 128, 44, 70,
+ 147, 229, 167, 186, 96, 166, 255, 146,
+ 204, 224, 171, 149, 97, 102, 1, 165,
+ 39, 222, 56, 12, 191, 202, 111, 103,
+ 120, 24, 69, 100, 34, 164, 135, 197,
+ 225, 18, 40, 236, 131, 231, 140, 63,
+ 181, 170, 73, 244, 58, 25, 98, 183,
+ 75, 57, 176, 118, 30, 226, 37, 36,
+ 130, 33, 55, 26, 10, 161, 107, 38,
+ 221, 234, 201, 121, 249, 116, 143, 62,
+ 190, 59, 115, 93, 92, 228, 192, 109,
+ 51, 8, 47, 13, 117, 173, 214, 81,
+ 169, 241, 182, 162, 0, 95, 218, 23,
+ 248, 132, 48, 232, 136, 240, 28, 154,
+ 126, 208, 60, 11, 16, 105, 4, 163,
+ 172, 238, 254, 88, 180, 168, 212, 35,
+ };
+
+static unsigned int
+hashi (const char *string, int length)
+{
+ unsigned char c, h1;
+
+ assert (string != NULL);
+
+ for (h1 = 0; length-- > 0; h1 = shuffle[h1 ^ c])
+ {
+ c = *string++;
+ if (isupper (c))
+ c = tolower (c);
+ }
+ return h1;
+}
+
+/* Insert a new node into the table. It is not an error for an entry with
+ the same name to be already present in the table. The new entry will
+ be found when searching the table. When removed, the former entry
+ will be found on a subsequent search */
+void *
+h_insert (struct h_node **table, const char *name, int namelen, size_t size)
+{
+ unsigned int hv;
+ struct h_node *node;
+
+ assert (table != NULL && name != NULL);
+
+ if (namelen < 0)
+ namelen = strlen (name);
+ if (namelen == 0)
+ return NULL;
+ size += sizeof (struct h_node);
+ if ((node = malloc (size)) == NULL)
+ return NULL;
+ memset (node, 0, size);
+ if ((node->name = malloc (namelen)) == NULL)
+ {
+ free (node);
+ return NULL;
+ }
+ memcpy (node->name, name, namelen);
+ hv = hashi (node->name, namelen);
+ node->next = table[hv];
+ table[hv] = node;
+ return node + 1;
+}
+
+/* Remove the node from the table.
+ */
+void
+h_remove (struct h_node **table, void *data)
+{
+ struct h_node *node = (struct h_node *) data - 1;
+ unsigned int hv;
+ struct h_node *p;
+
+ assert (table != NULL && node != NULL);
+
+ hv = hashi (node->name, strlen (node->name));
+ if (table[hv] == node)
+ table[hv] = node->next;
+ else
+ for (p = table[hv]; p != NULL; p = p->next)
+ if (p->next == node)
+ {
+ p->next = node->next;
+ node->next = NULL;
+ break;
+ }
+ free (node->name);
+ free (node);
+}
+
+/* Search for a node in the table.
+ */
+void *
+h_search (struct h_node **table, const char *name, int namelen)
+{
+ struct h_node *p;
+
+ assert (table != NULL && name != NULL);
+
+ if (namelen < 0)
+ namelen = strlen (name);
+ for (p = table[hashi (name, namelen)]; p != NULL; p = p->next)
+ if (strncasecmp (name, p->name, namelen) == 0)
+ return p + 1;
+ return NULL;
+}
+
+/* For each entry in the hash table, call the specified callback.
+ Entries are located in no particular order. */
+void
+h_enumerate (struct h_node **table,
+ void (*cb) (const char *name, void *data, void *arg), void *arg)
+{
+ struct h_node *p;
+ int i;
+
+ assert (table != NULL && cb != NULL);
+
+ for (i = 0; i < HASHSIZE; i++)
+ for (p = table[i]; p != NULL; p = p->next)
+ (*cb) (p->name, p + 1, arg);
+}
+
+/* Create a new hash table.
+ */
+struct h_node **
+h_create (void)
+{
+ return calloc (HASHSIZE, sizeof (struct h_node *));
+}
+
+/* Destroy the hash table. This frees all memory allocated to the table,
+ nodes and names. It also calls the callback function for each item in the
+ table just before freeing its other resources.
+ */
+void
+h_destroy (struct h_node **table,
+ void (*cb) (const char *name, void *data, void *arg), void *arg)
+{
+ struct h_node *p, *next;
+ int i;
+
+ assert (table != NULL);
+
+ for (i = 0; i < HASHSIZE; i++)
+ for (p = table[i]; p != NULL; p = next)
+ {
+ next = p->next;
+ if (cb != NULL)
+ (*cb) (p->name, p + 1, arg);
+ free (p->name);
+ free (p);
+ }
+ free (table);
+}
+
diff --git a/htable.h b/htable.h
new file mode 100644
index 0000000..64b4060
--- /dev/null
+++ b/htable.h
@@ -0,0 +1,39 @@
+#ifndef _htable
+#define _htable
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+struct h_node;
+
+void *h_insert (struct h_node **table,
+ const char *name, int namelen, size_t size);
+void h_remove (struct h_node **table, void *data);
+void *h_search (struct h_node **table, const char *name, int namelen);
+void h_enumerate (struct h_node **table,
+ void (*cb) (const char *name, void *data, void *arg),
+ void *arg);
+struct h_node **h_create (void);
+void h_destroy (struct h_node **table,
+ void (*cb) (const char *name, void *data, void *arg),
+ void *arg);
+
+#endif
diff --git a/libesmtp-config.in b/libesmtp-config.in
new file mode 100644
index 0000000..837f369
--- /dev/null
+++ b/libesmtp-config.in
@@ -0,0 +1,87 @@
+#! /bin/sh
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+
+usage()
+{
+ cat <<EOF
+Usage: libesmtp-config [OPTION]
+
+Known values for OPTION are:
+
+ --prefix=DIR change libesmtp prefix [default $prefix]
+ --libs print library linking information
+ --cflags print pre-processor and compiler flags
+ --plugindir print authentication plugin location
+ --help display this help and exit
+ --version output version information
+ --numeric-version output version information
+ --lib-version output library version
+EOF
+
+ exit $1
+}
+
+if test $# -eq 0; then
+ usage 1
+fi
+
+cflags=false
+libs=false
+
+while test $# -gt 0; do
+ case "$1" in
+ -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+ *) optarg= ;;
+ esac
+
+ case "$1" in
+ --prefix=*)
+ prefix=$optarg
+ ;;
+
+ --prefix)
+ echo $prefix
+ ;;
+
+ --version)
+ echo @VERSION@
+ exit 0
+ ;;
+
+ --numeric-version)
+ echo @VERSION@ | awk -F '[a-z.]*' '{ print ($1 * 1000 + $2) * 1000 + $3 }'
+ exit 0
+ ;;
+
+ --lib-version)
+ echo @LIBESMTP_VERSION@
+ exit 0
+ ;;
+
+ --help)
+ usage 0
+ ;;
+
+ --cflags)
+ echo -I@includedir@ @PTHREAD_CFLAGS@
+ ;;
+
+ --libs)
+ echo @PTHREAD_LDFLAGS@ -L@libdir@ -lesmtp @LIBS@ @PTHREAD_LIBS@
+ ;;
+
+ --plugindir)
+ echo @plugindir@
+ ;;
+
+ *)
+ usage
+ exit 1
+ ;;
+ esac
+ shift
+done
+
+exit 0
diff --git a/libesmtp-private.h b/libesmtp-private.h
new file mode 100644
index 0000000..7e242f7
--- /dev/null
+++ b/libesmtp-private.h
@@ -0,0 +1,256 @@
+#ifndef _libesmtp_private_h
+#define _libesmtp_private_h
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stddef.h> /* for size_t */
+
+#ifdef USE_TLS
+#include <openssl/ssl.h>
+#endif
+#include "libesmtp.h" /* The library needs the public declarations too! */
+#include "message-source.h"
+#include "concatenate.h"
+#include "auth-client.h"
+
+/* SMTP Extensions */
+
+#define _BIT(n) (1 << (n))
+
+#define EXT_ENHANCEDSTATUSCODES _BIT(0) /* RFC 1893, RFC 2034 */
+#define EXT_PIPELINING _BIT(1) /* RFC 2920 */
+#define EXT_DSN _BIT(2) /* RFC 1891 */
+#define EXT_AUTH _BIT(3) /* RFC 2554 AUTH using SASL */
+#define EXT_STARTTLS _BIT(4) /* RFC 3207 */
+#define EXT_SIZE _BIT(5) /* RFC 1870 */
+#define EXT_CHUNKING _BIT(6) /* RFC 3030 */
+#define EXT_BINARYMIME _BIT(7) /* RFC 3030 */
+#define EXT_8BITMIME _BIT(8) /* RFC 1652 */
+#define EXT_DELIVERBY _BIT(9) /* RFC 2852 */
+#define EXT_ETRN _BIT(10)/* RFC 1985 */
+#define EXT_XUSR _BIT(11)/* sendmail */
+#define EXT_XEXCH50 _BIT(12)/* exchange */
+
+struct smtp_session
+ {
+ /* Local info */
+ char *localhost; /* Domain name of localhost */
+
+ /* MTA */
+ char *host; /* Host domain name of SMTP server */
+ const char *port; /* Port number - default to 587 */
+
+ /* Application data */
+ void *application_data; /* Pointer to data maintained by app */
+
+ /* Messages */
+ struct smtp_message *messages; /* list of messages to submit */
+ struct smtp_message *end_messages;
+
+ /* Protocol events */
+ smtp_eventcb_t event_cb; /* Protocol event callback */
+ void *event_cb_arg; /* Argument for above */
+
+ /* Protocol monitor */
+ smtp_monitorcb_t monitor_cb; /* Protocol monitor callback */
+ void *monitor_cb_arg; /* Argument for above */
+ int monitor_cb_headers; /* Monitor views message headers */
+
+ /* Variables used by the protocol state engine */
+ int cmd_state, rsp_state;
+ struct smtp_message *current_message;
+ struct smtp_recipient *cmd_recipient;
+ struct smtp_recipient *rsp_recipient;
+ msg_source_t msg_source;
+
+ /* SMTP timeouts */
+ long greeting_timeout; /* default 5 minutes */
+ long envelope_timeout; /* default 5 minutes */
+ long data_timeout; /* default 2 minutes */
+ long transfer_timeout; /* default 3 minutes */
+ long data2_timeout; /* default 10 minutes */
+
+ /* Status */
+ smtp_status_t mta_status; /* Status from MTA greeting */
+
+ /* Protocol extensions */
+ unsigned long extensions;
+ unsigned long required_extensions;
+ unsigned long size_limit; /* RFC 1870 */
+ long min_by_time; /* RFC 2852 */
+
+ /* Interface to RFC 2554 AUTH and SASL */
+ auth_context_t auth_context;
+ struct mechanism *auth_mechanisms;
+ struct mechanism *current_mechanism;
+
+#ifdef USE_ETRN
+ /* Interface to RFC 1985 ETRN extension */
+ struct smtp_etrn_node *etrn_nodes;
+ struct smtp_etrn_node *end_etrn_nodes;
+ struct smtp_etrn_node *cmd_etrn_node;
+ struct smtp_etrn_node *rsp_etrn_node;
+#endif
+
+#ifdef USE_TLS
+ /* Interface to RFC 3207 STARTTLS extension */
+ enum starttls_option starttls_enabled;
+ SSL_CTX *starttls_ctx;
+#endif
+
+#ifdef USE_CHUNKING
+ int bdat_pipelined;
+#endif
+
+ /* Miscellaneous options and flags */
+ unsigned int try_fallback_server : 1;
+ unsigned int require_all_recipients : 1;
+ unsigned int authenticated : 1;
+#ifdef USE_CHUNKING
+ unsigned int bdat_abort_pipeline : 1;
+ unsigned int bdat_last_issued : 1;
+#endif
+#ifdef USE_TLS
+ unsigned int using_tls : 1;
+#endif
+ };
+
+struct smtp_message
+ {
+ struct smtp_message *next;
+ struct smtp_session *session; /* Back reference */
+
+ /* Application data */
+ void *application_data; /* Pointer to data maintained by app */
+
+ /* Reverse Path */
+ char *reverse_path_mailbox; /* Reverse path */
+
+ /* Status */
+ smtp_status_t reverse_path_status; /* Reverse path status from MAIL */
+ smtp_status_t message_status; /* Message status from DATA/BDAT */
+
+ /* Recipients */
+ struct smtp_recipient *recipients; /* List of recipients */
+ struct smtp_recipient *end_recipients;
+ int valid_recipients; /* Valid recipients in this session */
+ int failed_recipients; /* Failed recipients in this session */
+
+ /* Headers */
+ struct rfc2822_header *headers; /* List of headers to add to message */
+ struct rfc2822_header *end_headers;
+ struct rfc2822_header *current_header;
+ struct h_node **hdr_action; /* Hash table for header action */
+ struct catbuf hdr_buffer; /* Buffer for printing headers */
+
+ /* Message */
+ smtp_messagecb_t cb; /* Transfer message from app. */
+ void *cb_arg; /* Argument for above */
+
+ /* DSN (RFC 1891) */
+ char *dsn_envid; /* envelope identifier */
+ enum ret_flags dsn_ret; /* return headers or entire message */
+
+ /* SIZE (RFC 1870) */
+ unsigned long size_estimate;
+
+ /* DELIVERBY (RFC 2852 ) */
+ long by_time;
+ enum by_mode by_mode;
+ int by_trace;
+
+ /* 8BITMIME (RFC 1652) */
+ enum e8bitmime_body e8bitmime;
+ };
+
+struct smtp_recipient
+ {
+ struct smtp_recipient *next;
+ struct smtp_message *message; /* Back reference */
+
+ /* Application data */
+ void *application_data; /* Pointer to data maintained by app */
+
+ /* Recipient Info */
+ char *mailbox; /* Envelope address */
+ smtp_status_t status; /* Recipient status from RCPT */
+ unsigned complete : 1; /* Sent OK or permanent failure */
+ /* more per recipient stuff */
+
+ /* DSN - (RFC 1891) */
+ char *dsn_addrtype; /* original recipient address type */
+ char *dsn_orcpt; /* original recipient */
+ enum notify_flags dsn_notify; /* notification options */
+ };
+
+#define APPEND_LIST(start,end,item) do { \
+ if ((start) == NULL) \
+ (start) = (item); \
+ else \
+ (end)->next = (item); \
+ (end) = (item); \
+ (item)->next = NULL; \
+ } while (0)
+
+/* RFC 2822 minimum timeouts */
+
+#define GREETING_DEFAULT ( 5 * 60l * 1000l)
+#define ENVELOPE_DEFAULT ( 5 * 60l * 1000l)
+#define DATA_DEFAULT ( 2 * 60l * 1000l)
+#define TRANSFER_DEFAULT ( 3 * 60l * 1000l)
+#define DATA2_DEFAULT (10 * 60l * 1000l)
+
+/* protocol.c */
+
+int initial_transaction_state (smtp_session_t session);
+int next_message (smtp_session_t session);
+
+/* errors.c */
+
+void set_error (int code);
+void set_errno(int code);
+void set_herror (int code);
+int do_session (smtp_session_t session);
+void reset_status (struct smtp_status *status);
+
+/* smtp-auth.c */
+
+void set_auth_mechanisms (smtp_session_t session, const char *mechanisms);
+void destroy_auth_mechanisms (smtp_session_t session);
+int select_auth_mechanism (smtp_session_t session);
+
+#ifdef USE_TLS
+/* smtp-tls.c */
+
+int select_starttls (smtp_session_t session);
+void destroy_starttls_context (smtp_session_t session);
+#endif
+
+#ifdef USE_ETRN
+/* smtp-etrn.c */
+
+int check_etrn (smtp_session_t session);
+void destroy_etrn_nodes (smtp_session_t session);
+
+#endif
+
+#endif
diff --git a/libesmtp.h b/libesmtp.h
new file mode 100644
index 0000000..4fc2692
--- /dev/null
+++ b/libesmtp.h
@@ -0,0 +1,317 @@
+#ifndef _libesmtp_h
+#define _libesmtp_h
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* *********************************************************************
+ * ***************************** IMPORTANT *****************************
+ * *********************************************************************
+ *
+ * This API is intended for use as an ESMTP client within a Mail User
+ * Agent (MUA) or other program that wishes to submit mail to a
+ * preconfigured Mail Transport Agent (MTA).
+ *
+ * ***** IT IS NOT INTENDED FOR USE IN CODE THAT IMPLEMENTS AN MTA *****
+ *
+ * In particular, the CNAME, MX and A/AAAA lookup rules an MTA must
+ * use to locate the next hop MTA are not implemented.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct smtp_session *smtp_session_t;
+typedef struct smtp_message *smtp_message_t;
+typedef struct smtp_recipient *smtp_recipient_t;
+
+int smtp_version (void *buf, size_t len, int what);
+
+smtp_session_t smtp_create_session (void);
+smtp_message_t smtp_add_message (smtp_session_t session);
+typedef void (*smtp_enumerate_messagecb_t) (smtp_message_t message, void *arg);
+int smtp_enumerate_messages (smtp_session_t session,
+ smtp_enumerate_messagecb_t cb, void *arg);
+int smtp_set_server (smtp_session_t session, const char *hostport);
+int smtp_set_hostname (smtp_session_t session, const char *hostname);
+int smtp_set_reverse_path (smtp_message_t message, const char *mailbox);
+smtp_recipient_t smtp_add_recipient (smtp_message_t message,
+ const char *mailbox);
+typedef void (*smtp_enumerate_recipientcb_t) (smtp_recipient_t recipient,
+ const char *mailbox, void *arg);
+int smtp_enumerate_recipients (smtp_message_t message,
+ smtp_enumerate_recipientcb_t cb, void *arg);
+int smtp_set_header (smtp_message_t message, const char *header, ...);
+enum header_option
+ {
+ Hdr_OVERRIDE,
+ Hdr_PROHIBIT
+ /* add new options here */
+ };
+int smtp_set_header_option (smtp_message_t message, const char *header,
+ enum header_option option, ...);
+int smtp_set_resent_headers (smtp_message_t message, int onoff);
+typedef const char *(*smtp_messagecb_t) (void **ctx, int *len, void *arg);
+int smtp_set_messagecb (smtp_message_t message,
+ smtp_messagecb_t cb, void *arg);
+enum
+ {
+ /* Protocol progress */
+ SMTP_EV_CONNECT,
+ SMTP_EV_MAILSTATUS,
+ SMTP_EV_RCPTSTATUS,
+ SMTP_EV_MESSAGEDATA,
+ SMTP_EV_MESSAGESENT,
+ SMTP_EV_DISCONNECT,
+
+ /* Protocol extension progress */
+ SMTP_EV_ETRNSTATUS = 1000,
+
+ /* Required extensions */
+ SMTP_EV_EXTNA_DSN = 2000,
+ SMTP_EV_EXTNA_8BITMIME,
+ SMTP_EV_EXTNA_STARTTLS,
+ SMTP_EV_EXTNA_ETRN,
+ SMTP_EV_EXTNA_CHUNKING,
+ SMTP_EV_EXTNA_BINARYMIME,
+
+ /* Extensions specific events */
+ SMTP_EV_DELIVERBY_EXPIRED = 3000,
+
+ /* STARTTLS */
+ SMTP_EV_WEAK_CIPHER = 3100,
+ SMTP_EV_STARTTLS_OK,
+ SMTP_EV_INVALID_PEER_CERTIFICATE,
+ SMTP_EV_NO_PEER_CERTIFICATE,
+ SMTP_EV_WRONG_PEER_CERTIFICATE,
+ SMTP_EV_NO_CLIENT_CERTIFICATE,
+ SMTP_EV_UNUSABLE_CLIENT_CERTIFICATE,
+ SMTP_EV_UNUSABLE_CA_LIST
+ };
+typedef void (*smtp_eventcb_t) (smtp_session_t session, int event_no,
+ void *arg, ...);
+int smtp_set_eventcb (smtp_session_t session, smtp_eventcb_t cb, void *arg);
+typedef void (*smtp_monitorcb_t) (const char *buf, int buflen,
+ int writing, void *arg);
+int smtp_set_monitorcb (smtp_session_t session, smtp_monitorcb_t cb, void *arg,
+ int headers);
+int smtp_start_session (smtp_session_t session);
+int smtp_destroy_session (smtp_session_t session);
+
+struct smtp_status
+ {
+ int code; /* SMTP protocol status code */
+ char *text; /* Text from the server */
+ int enh_class; /* RFC 2034 enhanced status code triplet */
+ int enh_subject;
+ int enh_detail;
+ };
+typedef struct smtp_status smtp_status_t;
+
+const smtp_status_t *smtp_message_transfer_status (smtp_message_t message);
+const smtp_status_t *smtp_reverse_path_status (smtp_message_t message);
+int smtp_message_reset_status (smtp_message_t recipient);
+const smtp_status_t *smtp_recipient_status (smtp_recipient_t recipient);
+int smtp_recipient_check_complete (smtp_recipient_t recipient);
+int smtp_recipient_reset_status (smtp_recipient_t recipient);
+int smtp_errno (void);
+char *smtp_strerror (int error, char buf[], size_t buflen);
+
+void *smtp_set_application_data (smtp_session_t session, void *data);
+void *smtp_get_application_data (smtp_session_t session);
+void *smtp_message_set_application_data (smtp_message_t message, void *data);
+void *smtp_message_get_application_data (smtp_message_t message);
+void *smtp_recipient_set_application_data (smtp_recipient_t recipient,
+ void *data);
+void *smtp_recipient_get_application_data (smtp_recipient_t recipient);
+
+int smtp_option_require_all_recipients (smtp_session_t session, int state);
+
+#ifdef _auth_client_h
+int smtp_auth_set_context (smtp_session_t session, auth_context_t context);
+#endif
+
+/*
+ Standard callbacks for reading the message from the application.
+ */
+
+const char *_smtp_message_fp_cb (void **ctx, int *len, void *arg);
+#define smtp_set_message_fp(message,fp) \
+ smtp_set_messagecb ((message), _smtp_message_fp_cb, (fp))
+
+const char *_smtp_message_str_cb (void **ctx, int *len, void *arg);
+#define smtp_set_message_str(message,str) \
+ smtp_set_messagecb ((message), _smtp_message_str_cb, (str))
+
+/* Protocol timeouts */
+enum rfc2822_timeouts
+ {
+ Timeout_GREETING,
+ Timeout_ENVELOPE,
+ Timeout_DATA,
+ Timeout_TRANSFER,
+ Timeout_DATA2
+ };
+#define Timeout_OVERRIDE_RFC2822_MINIMUM 0x1000
+long smtp_set_timeout (smtp_session_t session, int which, long value);
+
+/****************************************************************************
+ * The following APIs relate to SMTP extensions. Note that not all
+ * supported extensions have corresponding API functions.
+ ****************************************************************************/
+
+/*
+ RFC 1891. Delivery Status Notification (DSN)
+ */
+
+enum ret_flags { Ret_NOTSET, Ret_FULL, Ret_HDRS };
+int smtp_dsn_set_ret (smtp_message_t message, enum ret_flags flags);
+int smtp_dsn_set_envid (smtp_message_t message, const char *envid);
+
+enum notify_flags
+ {
+ Notify_NOTSET,
+ Notify_NEVER = -1,
+ Notify_SUCCESS = 1,
+ Notify_FAILURE = 2,
+ Notify_DELAY = 4
+ };
+int smtp_dsn_set_notify (smtp_recipient_t recipient, enum notify_flags flags);
+int smtp_dsn_set_orcpt (smtp_recipient_t recipient,
+ const char *address_type, const char *address);
+
+/*
+ RFC 1870. SMTP Size extension.
+ */
+
+int smtp_size_set_estimate (smtp_message_t message, unsigned long size);
+
+/*
+ RFC 1652. 8bit-MIME Transport
+ */
+enum e8bitmime_body
+ {
+ E8bitmime_NOTSET,
+ E8bitmime_7BIT,
+ E8bitmime_8BITMIME,
+ E8bitmime_BINARYMIME
+ };
+int smtp_8bitmime_set_body (smtp_message_t message, enum e8bitmime_body body);
+
+/*
+ RFC 2852. Deliver By
+ */
+enum by_mode
+ {
+ By_NOTSET,
+ By_NOTIFY,
+ By_RETURN
+ };
+int smtp_deliverby_set_mode (smtp_message_t message,
+ long time, enum by_mode mode, int trace);
+
+/*
+ RFC 3207. SMTP Starttls extension.
+ */
+enum starttls_option
+ {
+ Starttls_DISABLED,
+ Starttls_ENABLED,
+ Starttls_REQUIRED
+ };
+int smtp_starttls_enable (smtp_session_t session, enum starttls_option how);
+
+/* Only delare this if the app has incuded <openssl/ssl.h> which
+ defines the symbol tested. */
+#ifdef SSL_SESSION_ASN1_VERSION
+int smtp_starttls_set_ctx (smtp_session_t session, SSL_CTX *ctx);
+#endif
+
+/* This is cleverly chosen to be the same as the OpenSSL declaration */
+typedef int (*smtp_starttls_passwordcb_t) (char *buf, int buflen,
+ int rwflag, void *arg);
+int smtp_starttls_set_password_cb (smtp_starttls_passwordcb_t cb, void *arg);
+
+/*
+ RFC 1985. Remote Message Queue Starting (ETRN)
+ */
+
+typedef struct smtp_etrn_node *smtp_etrn_node_t;
+smtp_etrn_node_t smtp_etrn_add_node (smtp_session_t session,
+ int option, const char *node);
+typedef void (*smtp_etrn_enumerate_nodecb_t) (smtp_etrn_node_t node,
+ int option, const char *domain,
+ void *arg);
+int smtp_etrn_enumerate_nodes (smtp_session_t session,
+ smtp_etrn_enumerate_nodecb_t cb, void *arg);
+const smtp_status_t *smtp_etrn_node_status (smtp_etrn_node_t node);
+void *smtp_etrn_set_application_data (smtp_etrn_node_t node, void *data);
+void *smtp_etrn_get_application_data (smtp_etrn_node_t node);
+
+#ifdef __cplusplus
+};
+#endif
+
+#define SMTPAPI_CHECK_ARGS(test,ret) \
+ do \
+ if (!(test)) { \
+ set_error (SMTP_ERR_INVAL); \
+ return ret; \
+ } \
+ while (0)
+
+
+#define SMTP_ERR_NOTHING_TO_DO 2
+#define SMTP_ERR_DROPPED_CONNECTION 3
+#define SMTP_ERR_INVALID_RESPONSE_SYNTAX 4
+#define SMTP_ERR_STATUS_MISMATCH 5
+#define SMTP_ERR_INVALID_RESPONSE_STATUS 6
+#define SMTP_ERR_INVAL 7
+#define SMTP_ERR_EXTENSION_NOT_AVAILABLE 8
+
+/* Deprecated - these will be removed in a future release */
+#define SMTP_ERR_HOST_NOT_FOUND SMTP_ERR_EAI_ADDRFAMILY
+#define SMTP_ERR_NO_ADDRESS SMTP_ERR_EAI_NODATA
+#define SMTP_ERR_NO_RECOVERY SMTP_ERR_EAI_FAIL
+#define SMTP_ERR_TRY_AGAIN SMTP_ERR_EAI_AGAIN
+
+/* libESMTP versions of some getaddrinfo error numbers */
+#define SMTP_ERR_EAI_AGAIN 12
+#define SMTP_ERR_EAI_FAIL 11
+#define SMTP_ERR_EAI_MEMORY 13
+#define SMTP_ERR_EAI_ADDRFAMILY 9
+#define SMTP_ERR_EAI_NODATA 10
+#define SMTP_ERR_EAI_FAMILY 14
+#define SMTP_ERR_EAI_BADFLAGS 15
+#define SMTP_ERR_EAI_NONAME 16
+#define SMTP_ERR_EAI_SERVICE 17
+#define SMTP_ERR_EAI_SOCKTYPE 18
+
+#define SMTP_ERR_UNTERMINATED_RESPONSE 19
+#define SMTP_ERR_CLIENT_ERROR 20
+
+/* Protocol monitor callback. Values for writing */
+#define SMTP_CB_READING 0
+#define SMTP_CB_WRITING 1
+#define SMTP_CB_HEADERS 2
+
+#endif
diff --git a/libesmtp.spec.in b/libesmtp.spec.in
new file mode 100644
index 0000000..a4d3da4
--- /dev/null
+++ b/libesmtp.spec.in
@@ -0,0 +1,82 @@
+# libesmtp.spec.in
+# Based on original file by Carlos Morgado <chbm@chbm.nu>
+# Updates from Pawel Salek <pawsa@theochem.kth.se>
+# Updates from Christophe Lambin <clambin@easynet.be>
+
+Summary: SMTP client library
+
+%define pkg @PACKAGE@
+%define ver @VERSION@
+%define rel 1
+%define prefix /usr
+%define plugindir %{prefix}/lib/esmtp-plugins
+%define openssl 0.9.6
+
+Name: %{pkg}
+Version: %{ver}
+Release: %{rel}
+
+Copyright: LGPL
+Group: System Environment/Libraries
+Summary: SMTP client library.
+Source: %{pkg}-%{ver}.tar.bz2
+
+URL: http://www.stafford.uklinux.net/libesmtp/
+BuildRoot: /var/tmp/%{pkg}-root
+Prefix: %{prefix}
+
+Packager: Brian Stafford <brian@stafford.uklinux.net>
+Provides: %{pkg}
+Docdir: %{_docdir}
+
+@RPM_REQUIRES@ @RPM_OPENSSL@
+@RPM_BUILDREQUIRES@ @RPM_OPENSSLDEVEL@
+
+%description
+LibESMTP is a library to manage posting (or submission of) electronic
+mail using SMTP to a preconfigured Mail Transport Agent (MTA) such as
+Exim. It may be used as part of a Mail User Agent (MUA) or another
+program that must be able to post electronic mail but where mail
+functionality is not the program's primary purpose.
+
+%package devel
+Group: Development/Libraries
+Summary: Headers and development libraries for libESMTP.
+Requires: %{pkg} = %{ver}
+@RPM_REQUIRES@ @RPM_OPENSSLDEVEL@
+
+%description devel
+The libesmtp-devel package contains headers and development libraries
+necessary for building programs against libesmtp.
+
+
+%prep
+%setup -T -b 0
+#%patch
+
+%build
+CFLAGS="$RPM_OPT_FLAGS" ./configure --prefix=%{prefix} --enable-all --with-auth-plugin-dir=%{plugindir} --enable-pthreads
+make
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make DESTDIR="$RPM_BUILD_ROOT" install
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+
+%files
+%defattr(-, root, root)
+%doc AUTHORS COPYING COPYING.GPL NEWS Notes README
+%{prefix}/lib/libesmtp.so.*
+%{plugindir}/sasl-*
+
+%files devel
+%defattr(-,root,root)
+%{prefix}/bin/libesmtp-config
+%{prefix}/include/*
+%{prefix}/lib/libesmtp.so
+%{prefix}/lib/libesmtp.la
+%{prefix}/lib/libesmtp.a
+
diff --git a/login/Makefile.am b/login/Makefile.am
new file mode 100644
index 0000000..91b974a
--- /dev/null
+++ b/login/Makefile.am
@@ -0,0 +1,11 @@
+## Process this file with automake to produce Makefile.in
+
+libdir = @plugindir@
+
+INCLUDES = -I@srcdir@
+
+lib_LTLIBRARIES = sasl-login.la
+AM_CFLAGS = @CFLAGS@ @EXTRA_CFLAGS@
+
+sasl_login_la_SOURCES = client-login.c
+sasl_login_la_LDFLAGS = -module -avoid-version
diff --git a/login/Makefile.in b/login/Makefile.in
new file mode 100644
index 0000000..c9c2f42
--- /dev/null
+++ b/login/Makefile.in
@@ -0,0 +1,476 @@
+# Makefile.in generated by automake 1.9.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+SOURCES = $(sasl_login_la_SOURCES)
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = login
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
+am__installdirs = "$(DESTDIR)$(libdir)"
+libLTLIBRARIES_INSTALL = $(INSTALL)
+LTLIBRARIES = $(lib_LTLIBRARIES)
+sasl_login_la_LIBADD =
+am_sasl_login_la_OBJECTS = client-login.lo
+sasl_login_la_OBJECTS = $(am_sasl_login_la_OBJECTS)
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(sasl_login_la_SOURCES)
+DIST_SOURCES = $(sasl_login_la_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CRAMMD5_LIBS = @CRAMMD5_LIBS@
+CRAMMD5_LTLIBOBJS = @CRAMMD5_LTLIBOBJS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DIST_PLUGINS = @DIST_PLUGINS@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+EXTRA_CFLAGS = @EXTRA_CFLAGS@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBESMTP_VERSION = @LIBESMTP_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBTOOL_DEPS = @LIBTOOL_DEPS@
+LIB_AGE = @LIB_AGE@
+LIB_CURRENT = @LIB_CURRENT@
+LIB_REVISION = @LIB_REVISION@
+LN_S = @LN_S@
+LTALLOCA = @LTALLOCA@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+NTLM_LIBS = @NTLM_LIBS@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LDFLAGS = @PTHREAD_LDFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+RANLIB = @RANLIB@
+RPM_BUILDREQUIRES = @RPM_BUILDREQUIRES@
+RPM_OPENSSL = @RPM_OPENSSL@
+RPM_OPENSSLDEVEL = @RPM_OPENSSLDEVEL@
+RPM_REQUIRES = @RPM_REQUIRES@
+SASL_PLUGINS = @SASL_PLUGINS@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+ac_ct_RANLIB = @ac_ct_RANLIB@
+ac_ct_STRIP = @ac_ct_STRIP@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
+am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @plugindir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+plugindir = @plugindir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+subdirs = @subdirs@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+INCLUDES = -I@srcdir@
+lib_LTLIBRARIES = sasl-login.la
+AM_CFLAGS = @CFLAGS@ @EXTRA_CFLAGS@
+sasl_login_la_SOURCES = client-login.c
+sasl_login_la_LDFLAGS = -module -avoid-version
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu login/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnu login/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(libdir)" || $(mkdir_p) "$(DESTDIR)$(libdir)"
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ if test -f $$p; then \
+ f=$(am__strip_dir) \
+ echo " $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(libdir)/$$f"; \
+ else :; fi; \
+ done
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @set -x; list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ p=$(am__strip_dir) \
+ echo " $(LIBTOOL) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$p'"; \
+ $(LIBTOOL) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$p"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+sasl-login.la: $(sasl_login_la_OBJECTS) $(sasl_login_la_DEPENDENCIES)
+ $(LINK) -rpath $(libdir) $(sasl_login_la_LDFLAGS) $(sasl_login_la_OBJECTS) $(sasl_login_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client-login.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ if $(LTCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+distclean-libtool:
+ -rm -f libtool
+uninstall-info-am:
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+ list='$(DISTFILES)'; for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+ esac; \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkdir_p) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)"; do \
+ test -z "$$dir" || $(mkdir_p) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-info-am uninstall-libLTLIBRARIES
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libLTLIBRARIES clean-libtool ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-exec \
+ install-exec-am install-info install-info-am \
+ install-libLTLIBRARIES install-man install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags uninstall uninstall-am uninstall-info-am \
+ uninstall-libLTLIBRARIES
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/login/client-login.c b/login/client-login.c
new file mode 100644
index 0000000..a8b6601
--- /dev/null
+++ b/login/client-login.c
@@ -0,0 +1,135 @@
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "auth-client.h"
+#include "auth-plugin.h"
+
+#define NELT(x) (sizeof x / sizeof x[0])
+
+static int login_init (void *pctx);
+static void login_destroy (void *ctx);
+static const char *login_response (void *ctx,
+ const char *challenge, int *len,
+ auth_interact_t interact, void *arg);
+
+const struct auth_client_plugin sasl_client =
+ {
+ /* Plugin information */
+ "LOGIN",
+ "Non-standard LOGIN mechanism",
+ /* Plugin instance */
+ login_init,
+ login_destroy,
+ /* Authentication */
+ login_response,
+ AUTH_PLUGIN_PLAIN,
+ /* Security Layer */
+ 0,
+ NULL,
+ NULL,
+ };
+
+static const struct auth_client_request client_request[] =
+ {
+ { "user", AUTH_CLEARTEXT | AUTH_USER, "User Name", 0, },
+ { "passphrase", AUTH_CLEARTEXT | AUTH_PASS, "Password", 0, },
+ };
+
+struct login_context
+ {
+ int state;
+ char *user;
+ int user_len;
+ char *pass;
+ int pass_len;
+ };
+
+static int
+login_init (void *pctx)
+{
+ struct login_context *login;
+
+ login = malloc (sizeof (struct login_context));
+ memset (login, 0, sizeof (struct login_context));
+
+ *(void **) pctx = login;
+ return 1;
+}
+
+static void
+login_destroy (void *ctx)
+{
+ struct login_context *login = ctx;
+
+ if (login->user != NULL)
+ {
+ memset (login->user, '\0', login->user_len);
+ free (login->user);
+ }
+ if (login->pass != NULL)
+ {
+ memset (login->pass, '\0', login->pass_len);
+ free (login->pass);
+ }
+ free (login);
+}
+
+static const char *
+login_response (void *ctx, const char *challenge __attribute__ ((unused)),
+ int *len, auth_interact_t interact, void *arg)
+{
+ struct login_context *login = ctx;
+ char *result[NELT (client_request)];
+
+ switch (login->state)
+ {
+ case 0:
+ /* Challenge is ignored (probably "Username:") */
+ if (!(*interact) (client_request, result, NELT (client_request), arg))
+ break;
+ login->user = strdup (result[0]);
+ login->user_len = strlen (login->user);
+ login->pass = strdup (result[1]);
+ login->pass_len = strlen (login->pass);
+ login->state = 1;
+ *len = login->user_len;
+ return login->user;
+
+ case 1:
+ /* Challenge is ignored (probably "Password:") */
+ login->state = -1;
+ *len = login->pass_len;
+ return login->pass;
+ }
+ *len = 0;
+ return NULL;
+}
+
diff --git a/memrchr.c b/memrchr.c
new file mode 100644
index 0000000..0460a93
--- /dev/null
+++ b/memrchr.c
@@ -0,0 +1,43 @@
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2004 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#undef HAVE_MEMRCHR
+#undef _GNU_SOURCE
+#undef _ISOC9X_SOURCE
+#undef _OSF_SOURCE
+#undef _XOPEN_SOURCE
+#undef __EXTENSIONS__
+#include <missing.h>
+
+void *
+memrchr (const void *a, int c, size_t len)
+{
+ const unsigned char *p = a;
+
+ for (p += len - 1; (const void *) p >= a; p--)
+ if (*p == c)
+ return (void *) p;
+ return (void *) 0;
+}
diff --git a/message-callbacks.c b/message-callbacks.c
new file mode 100644
index 0000000..0b71a8d
--- /dev/null
+++ b/message-callbacks.c
@@ -0,0 +1,92 @@
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* Standard callback functions for use by message-source.c
+ An application requiring anything more sophisticated than either of
+ these will need to supply its own callback. */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "libesmtp.h"
+
+/* Callback function to read the message from a file. The file MUST be
+ formatted according to RFC 2822 and lines MUST be terminated with the
+ canonical CRLF sequence. Furthermore, RFC 2821 line length
+ limitations must be observed (1000 octets maximum). */
+#define BUFLEN 8192
+
+const char *
+_smtp_message_fp_cb (void **ctx, int *len, void *arg)
+{
+ if (*ctx == NULL)
+ *ctx = malloc (BUFLEN);
+
+ if (len == NULL)
+ {
+ rewind ((FILE *) arg);
+ return NULL;
+ }
+
+ *len = fread (*ctx, 1, BUFLEN, (FILE *) arg);
+ return *ctx;
+}
+
+struct state
+ {
+ int state;
+ int length;
+ };
+
+const char *
+_smtp_message_str_cb (void **ctx, int *len, void *arg)
+{
+ const char *string = arg;
+ struct state *state;
+
+ if (*ctx == NULL)
+ *ctx = malloc (sizeof (struct state));
+ state = *ctx;
+
+ if (len == NULL)
+ {
+ state->state = 0;
+ state->length = strlen (string);
+ return NULL;
+ }
+
+ switch (state->state)
+ {
+ case 0:
+ state->state = 1;
+ *len = state->length;
+ break;
+
+ default:
+ *len = 0;
+ break;
+ }
+ return string;
+}
diff --git a/message-source.c b/message-source.c
new file mode 100644
index 0000000..b2ade6f
--- /dev/null
+++ b/message-source.c
@@ -0,0 +1,218 @@
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* Functions to read lines or blocks of text from the message source.
+ These functions allow the library to interface to the application
+ using a callback function. This is intended to allow the application
+ maximum flexibility in managing its message storage. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include "message-source.h"
+
+/* This is similar to code in siobuf.c */
+
+struct msg_source
+ {
+ /* Callback to fill the input buffer */
+ const char *(*cb) (void **ctx, int *len, void *arg);
+ void *arg;
+ void *ctx;
+
+ /* Input buffer */
+ const char *rp; /* input buffer pointer */
+ int rn; /* number of bytes unread in buffer */
+
+ /* Output buffer (used by msg_gets()) */
+ char *buf;
+ size_t nalloc;
+ };
+
+msg_source_t
+msg_source_create (void)
+{
+ msg_source_t source;
+
+ if ((source = malloc (sizeof (struct msg_source))) != NULL)
+ memset (source, 0, sizeof (struct msg_source));
+ return source;
+}
+
+void
+msg_source_destroy (msg_source_t source)
+{
+ assert (source != NULL);
+
+ if (source->ctx != NULL)
+ free (source->ctx);
+ if (source->buf != NULL)
+ free (source->buf);
+ free (source);
+}
+
+void
+msg_source_set_cb (msg_source_t source,
+ const char *(*cb) (void **ctx, int *len, void *arg),
+ void *arg)
+{
+ assert (source != NULL);
+
+ if (source->ctx != NULL)
+ {
+ free (source->ctx);
+ source->ctx = NULL;
+ }
+ source->cb = cb;
+ source->arg = arg;
+}
+
+/* Use the callback to get data from the message source.
+ */
+static int
+msg_fill (msg_source_t source)
+{
+ assert (source != NULL && source->cb != NULL);
+
+ source->rp = (*source->cb) (&source->ctx, &source->rn, source->arg);
+ return source->rn > 0;
+}
+
+void
+msg_rewind (msg_source_t source)
+{
+ assert (source != NULL && source->cb != NULL);
+
+ (*source->cb) (&source->ctx, NULL, source->arg);
+}
+
+/* Line oriented reader. An output buffer is allocated as required.
+ The return value is a pointer to the line and remains valid until the
+ next call to msg_gets (). The line is guaranteed to be terminated
+ with a \r\n. Len is set to the number of octets in the buffer.
+ The line is *not* terminated with a \0.
+
+ If concatenate is non-zero, the next line of input is concatenated
+ with the existing line and the return value points to the original
+ line in the buffer. In this case *len is the number of octets in
+ the buffer when called and is updated to the new count on return.
+ */
+const char *
+msg_gets (msg_source_t source, int *len, int concatenate)
+{
+ int lastc, c, buflen;
+ char *p, *nbuf;
+
+ assert (source != NULL && len != NULL);
+
+ if (source->rn <= 0 && !msg_fill (source))
+ return NULL;
+
+ if (source->buf == NULL)
+ {
+ source->nalloc = 1023; /* RFC 2821 max line length + slack */
+ source->buf = malloc (source->nalloc + 2);
+ if (source->buf == NULL)
+ return NULL;
+ }
+ p = source->buf;
+ buflen = source->nalloc;
+ if (concatenate)
+ {
+ p += *len;
+ buflen -= *len;
+ }
+
+ lastc = 0;
+ while (source->rn > 0 || msg_fill (source))
+ {
+ c = *source->rp++;
+ source->rn--;
+ if (buflen <= 0)
+ {
+ buflen = 512;
+ source->nalloc += buflen;
+ nbuf = realloc (source->buf, source->nalloc + 2);
+ if (nbuf == NULL)
+ {
+ free (source->buf);
+ return NULL;
+ }
+ p = nbuf + (p - source->buf);
+ source->buf = nbuf;
+ }
+ *p++ = c;
+ buflen--;
+ if (c == '\n' && lastc == '\r')
+ {
+ *len = p - source->buf;
+ return source->buf;
+ }
+ lastc = c;
+ }
+ /* Only get here if the input was not properly terminated with a \r\n.
+ The handling of the DATA command in protocol.c relies on the \n
+ so we add it here. This is why there is 2 characters of slack in
+ malloc and realloc above. */
+ if (lastc != '\r')
+ *p++ = '\r';
+ *p++ = '\n';
+ *len = p - source->buf;
+ return source->buf;
+}
+
+/* Return the next character in the source, i.e. the first character
+ that will be returned by the next call to msg_gets(). This is
+ currently only used to check for RFC 2822 header continuation lines.
+ It is not safe to use in conjunction with msg_getb().
+ */
+int
+msg_nextc (msg_source_t source)
+{
+ assert (source != NULL);
+
+ if (source->rn <= 0 && !msg_fill (source))
+ return -1;
+
+ return *source->rp;
+}
+
+/* Block oriented reader. The output buffer is not used for efficiency.
+ */
+const char *
+msg_getb (msg_source_t source, int *len)
+{
+ assert (source != NULL);
+
+ if (source->rn <= 0 && !msg_fill (source))
+ return NULL;
+
+ /* Just return whatever is in the input buffer */
+ *len = source->rn;
+ source->rn = 0;
+ return source->rp;
+}
diff --git a/message-source.h b/message-source.h
new file mode 100644
index 0000000..a57ea26
--- /dev/null
+++ b/message-source.h
@@ -0,0 +1,42 @@
+#ifndef _message_source_h
+#define _message_source_h
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* Functions to read lines or blocks of text from the message source.
+ These functions allow the library to interface to the application
+ using a callback function. This is intended to allow the application
+ maximum flexibility in managing its message storage. */
+
+typedef struct msg_source *msg_source_t;
+
+msg_source_t msg_source_create (void);
+void msg_source_destroy (msg_source_t source);
+void msg_source_set_cb (msg_source_t source,
+ const char *(*cb) (void **ctx, int *len, void *arg),
+ void *arg);
+void msg_rewind (msg_source_t source);
+const char *msg_gets (msg_source_t source, int *len, int concatenate);
+int msg_nextc (msg_source_t source);
+const char *msg_getb (msg_source_t source, int *len);
+
+#endif
diff --git a/missing.h b/missing.h
new file mode 100644
index 0000000..36cc703
--- /dev/null
+++ b/missing.h
@@ -0,0 +1,57 @@
+#ifndef _missing_h
+#define _missing_h
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2002-2004 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* Collect together some declarations that might not be present on
+ some systems */
+
+#ifndef HAVE_STRDUP
+char *strdup (const char *s1);
+#endif
+
+#ifndef HAVE_STRCASECMP
+int strcasecmp (const char *a, const char *b);
+#endif
+
+#ifndef HAVE_STRNCASECMP
+#include <sys/types.h>
+int strncasecmp (const char *a, const char *b, size_t len);
+#endif
+
+#ifndef HAVE_MEMRCHR
+#include <sys/types.h>
+void *memrchr (const void *a, int c, size_t len);
+#endif
+
+#ifndef HAVE_SNPRINTF
+#include <sys/types.h>
+int snprintf(char *s, size_t n, const char *format, ...);
+#endif
+
+#ifndef HAVE_VSNPRINTF
+#include <stdarg.h>
+#include <sys/types.h>
+int vsnprintf(char *s, size_t n, const char *format, va_list ap);
+#endif
+
+#endif
diff --git a/ntlm/Makefile.am b/ntlm/Makefile.am
new file mode 100644
index 0000000..a636e50
--- /dev/null
+++ b/ntlm/Makefile.am
@@ -0,0 +1,11 @@
+## Process this file with automake to produce Makefile.in
+
+libdir = @plugindir@
+
+INCLUDES = -I@srcdir@
+AM_CFLAGS = @CFLAGS@ @EXTRA_CFLAGS@
+
+lib_LTLIBRARIES = sasl-ntlm.la
+
+sasl_ntlm_la_SOURCES = client-ntlm.c ntlmdes.c ntlmstruct.c ntlm.h
+sasl_ntlm_la_LDFLAGS = -module -avoid-version @NTLM_LIBS@
diff --git a/ntlm/Makefile.in b/ntlm/Makefile.in
new file mode 100644
index 0000000..c7f5e6a
--- /dev/null
+++ b/ntlm/Makefile.in
@@ -0,0 +1,478 @@
+# Makefile.in generated by automake 1.9.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+SOURCES = $(sasl_ntlm_la_SOURCES)
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = ntlm
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
+am__installdirs = "$(DESTDIR)$(libdir)"
+libLTLIBRARIES_INSTALL = $(INSTALL)
+LTLIBRARIES = $(lib_LTLIBRARIES)
+sasl_ntlm_la_LIBADD =
+am_sasl_ntlm_la_OBJECTS = client-ntlm.lo ntlmdes.lo ntlmstruct.lo
+sasl_ntlm_la_OBJECTS = $(am_sasl_ntlm_la_OBJECTS)
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(sasl_ntlm_la_SOURCES)
+DIST_SOURCES = $(sasl_ntlm_la_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CRAMMD5_LIBS = @CRAMMD5_LIBS@
+CRAMMD5_LTLIBOBJS = @CRAMMD5_LTLIBOBJS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DIST_PLUGINS = @DIST_PLUGINS@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+EXTRA_CFLAGS = @EXTRA_CFLAGS@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBESMTP_VERSION = @LIBESMTP_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBTOOL_DEPS = @LIBTOOL_DEPS@
+LIB_AGE = @LIB_AGE@
+LIB_CURRENT = @LIB_CURRENT@
+LIB_REVISION = @LIB_REVISION@
+LN_S = @LN_S@
+LTALLOCA = @LTALLOCA@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+NTLM_LIBS = @NTLM_LIBS@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LDFLAGS = @PTHREAD_LDFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+RANLIB = @RANLIB@
+RPM_BUILDREQUIRES = @RPM_BUILDREQUIRES@
+RPM_OPENSSL = @RPM_OPENSSL@
+RPM_OPENSSLDEVEL = @RPM_OPENSSLDEVEL@
+RPM_REQUIRES = @RPM_REQUIRES@
+SASL_PLUGINS = @SASL_PLUGINS@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+ac_ct_RANLIB = @ac_ct_RANLIB@
+ac_ct_STRIP = @ac_ct_STRIP@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
+am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @plugindir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+plugindir = @plugindir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+subdirs = @subdirs@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+INCLUDES = -I@srcdir@
+AM_CFLAGS = @CFLAGS@ @EXTRA_CFLAGS@
+lib_LTLIBRARIES = sasl-ntlm.la
+sasl_ntlm_la_SOURCES = client-ntlm.c ntlmdes.c ntlmstruct.c ntlm.h
+sasl_ntlm_la_LDFLAGS = -module -avoid-version @NTLM_LIBS@
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu ntlm/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnu ntlm/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(libdir)" || $(mkdir_p) "$(DESTDIR)$(libdir)"
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ if test -f $$p; then \
+ f=$(am__strip_dir) \
+ echo " $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(libdir)/$$f"; \
+ else :; fi; \
+ done
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @set -x; list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ p=$(am__strip_dir) \
+ echo " $(LIBTOOL) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$p'"; \
+ $(LIBTOOL) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$p"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+sasl-ntlm.la: $(sasl_ntlm_la_OBJECTS) $(sasl_ntlm_la_DEPENDENCIES)
+ $(LINK) -rpath $(libdir) $(sasl_ntlm_la_LDFLAGS) $(sasl_ntlm_la_OBJECTS) $(sasl_ntlm_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client-ntlm.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntlmdes.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntlmstruct.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ if $(LTCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+distclean-libtool:
+ -rm -f libtool
+uninstall-info-am:
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+ list='$(DISTFILES)'; for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+ esac; \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkdir_p) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)"; do \
+ test -z "$$dir" || $(mkdir_p) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-info-am uninstall-libLTLIBRARIES
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libLTLIBRARIES clean-libtool ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-exec \
+ install-exec-am install-info install-info-am \
+ install-libLTLIBRARIES install-man install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags uninstall uninstall-am uninstall-info-am \
+ uninstall-libLTLIBRARIES
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/ntlm/client-ntlm.c b/ntlm/client-ntlm.c
new file mode 100644
index 0000000..3067f85
--- /dev/null
+++ b/ntlm/client-ntlm.c
@@ -0,0 +1,149 @@
+/*
+ * Authentication module for the Micr$oft NTLM mechanism.
+ *
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#define _XOPEN_SOURCE 500
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include "ntlm.h"
+#include "auth-client.h"
+#include "auth-plugin.h"
+
+#if 0
+/* This is handy for changing te value of the flags in the debugger. */
+unsigned int t1flags = TYPE1_FLAGS;
+#undef TYPE1_FLAGS
+#define TYPE1_FLAGS t1flags
+#endif
+
+#define NELT(x) (sizeof x / sizeof x[0])
+
+static int ntlm_init (void *pctx);
+static void ntlm_destroy (void *ctx);
+static const char *ntlm_response (void *ctx,
+ const char *challenge, int *len,
+ auth_interact_t interact, void *arg);
+
+const struct auth_client_plugin sasl_client =
+ {
+ /* Plugin information */
+ "NTLM",
+ "NTLM Authentication Mechanism (Microsoft)",
+ /* Plugin instance */
+ ntlm_init,
+ ntlm_destroy,
+ /* Authentication */
+ ntlm_response,
+ 0,
+ /* Security Layer */
+ 0,
+ NULL,
+ NULL,
+ };
+
+static const struct auth_client_request client_request[] =
+ {
+ { "domain", AUTH_CLEARTEXT | AUTH_REALM, "Domain", 0, },
+ { "user", AUTH_CLEARTEXT | AUTH_USER, "User Name", 0, },
+ { "passphrase", AUTH_PASS, "Pass Phrase", 0, },
+ };
+
+struct ntlm_context
+ {
+ int state;
+ char *result[NELT (client_request)];
+ char host[64];
+ char buf[256];
+ };
+
+static int
+ntlm_init (void *pctx)
+{
+ struct ntlm_context *context;
+
+ context = malloc (sizeof (struct ntlm_context));
+ memset (context, 0, sizeof (struct ntlm_context));
+
+ *(void **) pctx = context;
+ return 1;
+}
+
+static void
+ntlm_destroy (void *ctx)
+{
+ struct ntlm_context *context = ctx;
+
+ memset (context, 0, sizeof (struct ntlm_context));
+ free (context);
+}
+
+static const char *
+ntlm_response (void *ctx, const char *challenge, int *len,
+ auth_interact_t interact, void *arg)
+{
+ struct ntlm_context *context = ctx;
+ unsigned char nonce[8];
+ unsigned char lm_resp[24], nt_resp[24];
+ unsigned int flags;
+ char *domain = NULL;
+ char *p;
+
+ switch (context->state)
+ {
+ case 0: /* build the authentication request */
+ context->state = 1;
+ if (!(*interact) (client_request, context->result, NELT (client_request),
+ arg))
+ break;
+ gethostname (context->host, sizeof context->host);
+ if ((p = strchr (context->host, '.')) != NULL)
+ *p = '\0';
+ *len = ntlm_build_type_1 (context->buf, sizeof context->buf, TYPE1_FLAGS,
+ context->result[0], context->host);
+ return context->buf;
+
+ case 1: /* compute a response based om the challenge */
+ context->state = 2;
+ if (!ntlm_parse_type_2 (challenge, *len, &flags, nonce, &domain))
+ break;
+ ntlm_responses (lm_resp, nt_resp, nonce, context->result[2]);
+ *len = ntlm_build_type_3 (context->buf, sizeof context->buf,
+ flags, lm_resp, nt_resp,
+ context->result[0], context->result[1],
+ context->host);
+ if (domain != NULL)
+ free (domain);
+ return context->buf;
+ }
+ *len = 0;
+ return NULL;
+}
+
diff --git a/ntlm/ntlm.h b/ntlm/ntlm.h
new file mode 100644
index 0000000..50a49d6
--- /dev/null
+++ b/ntlm/ntlm.h
@@ -0,0 +1,52 @@
+#ifndef _ntlm_h
+#define _ntlm_h
+/*
+ * Authentication module for the Micr$oft NTLM mechanism.
+ *
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+char *lm_uccpy (char *dst, size_t dstlen, const char *src);
+unsigned char *nt_unicode (const char *string, size_t len);
+
+void lm_hash_password (unsigned char *hash, const char *pass);
+void nt_hash_password (unsigned char *hash, const char *pass);
+
+void ntlm_responses (unsigned char *lm_resp, unsigned char *nt_resp,
+ const unsigned char *challenge, const char *secret);
+
+size_t ntlm_build_type_1 (char *buf, size_t buflen, unsigned int flags,
+ const char *domain, const char *workstation);
+size_t ntlm_build_type_2 (char *buf, size_t buflen, unsigned int flags,
+ const unsigned char *nonce, const char *domain);
+size_t ntlm_build_type_3 (char *buf, size_t buflen,
+ unsigned int flags,
+ const unsigned char *lm_resp,
+ const unsigned char *nt_resp,
+ const char *domain, const char *user,
+ const char *workstation);
+
+size_t ntlm_parse_type_2 (const char *buf, size_t buflen, unsigned int *flags,
+ unsigned char *nonce, char **domain);
+
+#define TYPE1_FLAGS 0x8202u
+
+#endif
diff --git a/ntlm/ntlmdes.c b/ntlm/ntlmdes.c
new file mode 100644
index 0000000..57a39d1
--- /dev/null
+++ b/ntlm/ntlmdes.c
@@ -0,0 +1,157 @@
+/*
+ * Authentication module for the Micr$oft NTLM mechanism.
+ *
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <openssl/des.h>
+#include <openssl/md4.h>
+
+#include "ntlm.h"
+
+static void
+lm_deshash (void *result, const_des_cblock *iv, const void *secret)
+{
+ des_cblock key;
+ des_key_schedule ks;
+ unsigned char key_56[8];
+ size_t len;
+
+ /* copy and pad the secret */
+ len = strlen (secret);
+ if (len > sizeof key_56)
+ len = sizeof key_56;
+ memcpy (key_56, secret, len);
+ if (sizeof key_56 - len > 0)
+ memset (key_56 + len, 0, sizeof key_56 - len);
+
+ /* convert 56 bit key to the 64 bit */
+ key[0] = key_56[0];
+ key[1] = (key_56[0] << 7) | (key_56[1] >> 1);
+ key[2] = (key_56[1] << 6) | (key_56[2] >> 2);
+ key[3] = (key_56[2] << 5) | (key_56[3] >> 3);
+ key[4] = (key_56[3] << 4) | (key_56[4] >> 4);
+ key[5] = (key_56[4] << 3) | (key_56[5] >> 5);
+ key[6] = (key_56[5] << 2) | (key_56[6] >> 6);
+ key[7] = (key_56[6] << 1);
+
+ des_set_odd_parity (&key);
+ des_set_key (&key, ks);
+ des_ecb_encrypt (iv, result, ks, DES_ENCRYPT);
+
+ /* paranoia */
+ memset (key, 0, sizeof key);
+ memset (&ks, 0, sizeof ks);
+}
+
+/* Copy and convert to upper case. If supplied string is shorter than the
+ destination, zero pad the remainder. */
+char *
+lm_uccpy (char *dst, size_t dstlen, const char *src)
+{
+ char *p;
+ size_t len;
+
+ if ((len = strlen (src)) > dstlen)
+ len = dstlen;
+ for (p = dst; len > 0; p++, src++, len--)
+ *p = toupper (*src);
+ if (p < dst + dstlen)
+ memset (p, 0, dst + dstlen - p);
+ return dst;
+}
+
+/* create LanManager hashed password */
+void
+lm_hash_password (unsigned char *hash, const char *pass)
+{
+ static const_des_cblock iv = { 0x4B, 0x47, 0x53, 0x21,
+ 0x40, 0x23, 0x24, 0x25 };
+ char lmpass[14];
+
+ lm_uccpy (lmpass, sizeof lmpass, pass);
+ lm_deshash (hash, &iv, lmpass);
+ lm_deshash (hash + 8, &iv, lmpass + 7);
+ memset (lmpass, 0, sizeof lmpass);
+}
+
+/* convert to unicode */
+unsigned char *
+nt_unicode (const char *string, size_t len)
+{
+ unsigned char *uni, *pp;
+
+ uni = malloc (len * 2);
+ if ((pp = uni) != NULL)
+ while (len-- > 0)
+ {
+ *pp++ = (unsigned char) *string++;
+ *pp++ = 0;
+ }
+ return uni;
+}
+
+void
+nt_hash_password (unsigned char *hash, const char *pass)
+{
+ int len;
+ unsigned char *nt_pw;
+ MD4_CTX context;
+
+ len = strlen (pass);
+ if ((nt_pw = nt_unicode (pass, len)) == NULL)
+ return;
+
+ MD4_Init (&context);
+ MD4_Update (&context, nt_pw, 2 * len);
+ MD4_Final (hash, &context);
+ memset (&context, 0, sizeof context);
+ memset (nt_pw, 0, 2 * len);
+ free (nt_pw);
+}
+
+/* Use the server's 8 octet nonce and the secret to create the 24 octet
+ LanManager and NT responses. */
+void
+ntlm_responses (unsigned char *lm_resp, unsigned char *nt_resp,
+ const unsigned char *challenge, const char *secret)
+{
+ unsigned char hash[21];
+ des_cblock nonce;
+
+ memcpy (&nonce, challenge, sizeof nonce);
+
+ lm_hash_password (hash, secret);
+ memset (hash + 16, 0, 5);
+ lm_deshash (lm_resp, &nonce, hash);
+ lm_deshash (lm_resp + 8, &nonce, hash + 7);
+ lm_deshash (lm_resp + 16, &nonce, hash + 14);
+
+ nt_hash_password (hash, secret);
+ memset (hash + 16, 0, 5);
+ lm_deshash (nt_resp, &nonce, hash);
+ lm_deshash (nt_resp + 8, &nonce, hash + 7);
+ lm_deshash (nt_resp + 16, &nonce, hash + 14);
+ memset (hash, 0, sizeof hash);
+}
+
diff --git a/ntlm/ntlmstruct.c b/ntlm/ntlmstruct.c
new file mode 100644
index 0000000..dfb80de
--- /dev/null
+++ b/ntlm/ntlmstruct.c
@@ -0,0 +1,331 @@
+/*
+ * Authentication module for the Micr$oft NTLM mechanism.
+ *
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "ntlm.h"
+
+/* Must have at least 32 bits in an int (at least pending a more thorough
+ code review - this module is still experimental) */
+#if SIZEOF_UNSIGNED_INT < 32 / 8
+# error "unsigned int is less than 32 bits wide"
+#endif
+
+#if SIZEOF_UNSIGNED_SHORT == 16 / 8
+typedef unsigned short unsigned16_t;
+#else
+#include <sys/types.h>
+typedef uint16 unsigned16_t;
+#endif
+
+#if SIZEOF_UNSIGNED_INT == 32 / 8
+typedef unsigned int unsigned32_t;
+#elif SIZEOF_UNSIGNED_LONG == 32 / 8
+typedef unsigned long unsigned32_t;
+#else
+#include <sys/types.h>
+typedef uint32 unsigned32_t;
+#endif
+
+#ifdef WORDS_BIGENDIAN
+/* Everything in NTLM is little endian binary, therefore byte swapping
+ is needed on big endian platforms. For simplicity, always provide
+ byte swapping functions rather than trying to detect the local
+ platform's support. These functions make no effort to be efficient
+ since this code doesn't require efficient byte swapping. */
+
+#define SWAP(a,b) do { unsigned char s = u.swap[(a)]; \
+ u.swap[(a)] = u.swap[(b)]; \
+ u.swap[(b)] = s; } while (0)
+
+static unsigned16_t
+bswap_16 (unsigned16_t value)
+{
+ union { unsigned16_t val; unsigned char swap[2]; } u;
+
+ u.val = value;
+ SWAP(0,1);
+ return u.val;
+}
+
+static unsigned32_t
+bswap_32 (unsigned32_t value)
+{
+ union u32 { unsigned32_t val; unsigned char swap[4]; } u;
+
+ u.val = value;
+ SWAP(0,3);
+ SWAP(1,2);
+ return u.val;
+}
+
+#undef SWAP
+#endif
+
+static void
+write_uint16 (char *buf, size_t offset, unsigned int value)
+{
+ unsigned16_t i16 = value;
+
+ assert (sizeof i16 == 2);
+#ifdef WORDS_BIGENDIAN
+ i16 = bswap_16 (i16);
+#endif
+ memcpy (buf + offset, &i16, sizeof i16);
+}
+
+static inline void
+write_uint32 (char *buf, size_t offset, unsigned int value)
+{
+ unsigned32_t i32 = value;
+
+ assert (sizeof i32 == 4);
+#ifdef WORDS_BIGENDIAN
+ i32 = bswap_32 (i32);
+#endif
+ memcpy (buf + offset, &i32, sizeof i32);
+}
+
+static inline unsigned int
+read_uint16 (const char *buf, size_t offset)
+{
+ unsigned16_t i16;
+
+ assert (sizeof i16 == 2);
+ memcpy (&i16, buf + offset, sizeof i16);
+#ifdef WORDS_BIGENDIAN
+ i16 = bswap_16 (i16);
+#endif
+ return i16;
+}
+
+static inline unsigned int
+read_uint32 (const char *buf, size_t offset)
+{
+ unsigned32_t i32;
+
+ assert (sizeof i32 == 4);
+ memcpy (&i32, buf + offset, sizeof i32);
+#ifdef WORDS_BIGENDIAN
+ i32 = bswap_32 (i32);
+#endif
+ return i32;
+}
+
+static inline void
+write_string (char *buf, size_t offset, size_t *str_offset,
+ const void *data, size_t len)
+{
+ if (data == NULL)
+ len = 0;
+ write_uint16 (buf, offset, len);
+ write_uint16 (buf, offset + 2, len);
+ write_uint32 (buf, offset + 4, *str_offset);
+ if (len > 0)
+ memcpy (buf + *str_offset, data, len);
+ *str_offset += len;
+}
+
+/* Offsets into on-the-wire NTLM structures */
+#define PROTOCOL 0 /* "NTLMSSP" */
+#define MSGTYPE 8 /* 1..3 */
+/* Type 1 fields */
+#define T1FLAGS 12 /* 0xb203 */
+#define T1DOMAIN 16 /* domain to authenticate in */
+#define T1WKSTN 24 /* client workstation name */
+#define T1SIZE 32
+/* Type 2 fields */
+#define T2AUTHTARGET 12 /* domain/server */
+#define T2FLAGS 20 /* 0x8201 */
+#define T2NONCE 24 /* server challenge */
+#define T2RESERVED 32
+#define T2SIZE 40
+/* Type 3 fields */
+#define T3LMRESPONSE 12 /* lanmanager hash */
+#define T3NTRESPONSE 20 /* nt hash */
+#define T3DOMAIN 28 /* domain to authenticate against */
+#define T3USER 36 /* user name */
+#define T3WKSTN 44 /* client workstation name */
+#define T3SESSIONKEY 52 /* client workstation name */
+#define T3FLAGS 60 /* 0xb203 */
+#define T3SIZE 64
+
+static const char NTLMSSP[] = "NTLMSSP";
+
+/* Build a NTLM type 1 structure in the buffer.
+ domain - the NT domain the workstation belongs to
+ workstation - the NT (netbios) name of the workstation */
+size_t
+ntlm_build_type_1 (char *buf, size_t buflen, unsigned int flags,
+ const char *domain, const char *workstation)
+{
+ size_t offset = T1SIZE;
+ size_t len;
+ unsigned char *up;
+ char string[256];
+
+ if (buflen < offset)
+ return 0;
+ memcpy (buf, NTLMSSP, 8);
+ write_uint32 (buf, MSGTYPE, 1);
+ write_uint32 (buf, T1FLAGS, flags);
+ up = NULL;
+ len = 0;
+ if (domain != NULL)
+ {
+ len = strlen (domain);
+ if (offset + len > buflen)
+ return 0;
+ lm_uccpy (string, len, domain);
+ }
+ write_string (buf, T1DOMAIN, &offset, string, len);
+ up = NULL;
+ len = 0;
+ if (workstation != NULL)
+ {
+ len = strlen (workstation);
+ if (offset + len > buflen)
+ return 0;
+ lm_uccpy (string, len, workstation);
+ }
+ write_string (buf, T1WKSTN, &offset, string, len);
+ return offset;
+}
+
+/* Build a NTLM type 2 structure in the buffer */
+size_t
+ntlm_build_type_2 (char *buf, size_t buflen, unsigned int flags,
+ const unsigned char *nonce, const char *domain)
+{
+ size_t offset = T2SIZE;
+ size_t len;
+ char string[256];
+ unsigned char *up;
+
+ if (buflen < offset)
+ return 0;
+ memcpy (buf, NTLMSSP, 8);
+ write_uint32 (buf, MSGTYPE, 2);
+ up = NULL;
+ len = 0;
+ if (domain != NULL)
+ {
+ len = strlen (domain);
+ if (offset + 2 * len > buflen)
+ return 0;
+ up = nt_unicode (lm_uccpy (string, len, domain), 2 * len);
+ }
+ write_string (buf, T2AUTHTARGET, &offset, up, len);
+ if (up != NULL)
+ free (up);
+ write_uint32 (buf, T2FLAGS, flags);
+ memcpy (buf + T2NONCE, nonce, 8);
+ memset (buf + T2RESERVED, 0, 8);
+ return offset;
+}
+
+/* Build a NTLM type 3 structure in the buffer */
+size_t
+ntlm_build_type_3 (char *buf, size_t buflen, unsigned int flags,
+ const unsigned char *lm_resp, const unsigned char *nt_resp,
+ const char *domain, const char *user, const char *workstation)
+{
+ size_t offset = T3SIZE;
+ size_t len;
+ char string[256];
+ unsigned char *up;
+
+ if (buflen + 24 + 24 < offset)
+ return 0;
+ memcpy (buf, NTLMSSP, 8);
+ write_uint32 (buf, MSGTYPE, 3);
+ write_string (buf, T3LMRESPONSE, &offset, lm_resp, 24);
+ write_string (buf, T3NTRESPONSE, &offset, nt_resp, 24);
+ up = NULL;
+ len = 0;
+ if (domain != NULL)
+ {
+ len = strlen (domain);
+ if (offset + 2 * len > buflen)
+ return 0;
+ up = nt_unicode (lm_uccpy (string, len, domain), 2 * len);
+ }
+ write_string (buf, T3DOMAIN, &offset, up, 2 * len);
+ if (up != NULL)
+ free (up);
+ up = NULL;
+ len = 0;
+ if (user != NULL)
+ {
+ len = strlen (user);
+ if (offset + 2 * len > buflen)
+ return 0;
+ up = nt_unicode (lm_uccpy (string, len, user), 2 * len);
+ }
+ write_string (buf, T3USER, &offset, up, 2 * len);
+ if (up != NULL)
+ free (up);
+ up = NULL;
+ len = 0;
+ if (workstation != NULL)
+ {
+ len = strlen (workstation);
+ if (offset + 2 * len > buflen)
+ return 0;
+ up = nt_unicode (lm_uccpy (string, len, workstation), 2 * len);
+ }
+ write_string (buf, T3WKSTN, &offset, up, 2 * len);
+ if (up != NULL)
+ free (up);
+ write_string (buf, T3SESSIONKEY, &offset, NULL, 0);
+ write_uint32 (buf, T3FLAGS, flags);
+ return offset;
+}
+
+/****/
+
+/* Parse a NTLM type 2 structure in the buffer (partial implementation).
+ Verify that the packet is a type 2 structure and copy the nonce to the
+ supplied buffer (which must be eight bytes long) */
+size_t
+ntlm_parse_type_2 (const char *buf, size_t buflen, unsigned int *flags,
+ unsigned char *nonce, char **domain)
+{
+ if (buflen < T2SIZE)
+ return 0;
+ if (memcmp (buf, NTLMSSP, 8) != 0)
+ return 0;
+ if (read_uint32 (buf, MSGTYPE) != 2)
+ return 0;
+ *flags = read_uint32 (buf, T2FLAGS);
+ *domain = NULL;
+ memcpy (nonce, buf + T2NONCE, 8);
+ return 1;
+}
+
diff --git a/plain/Makefile.am b/plain/Makefile.am
new file mode 100644
index 0000000..b121379
--- /dev/null
+++ b/plain/Makefile.am
@@ -0,0 +1,11 @@
+## Process this file with automake to produce Makefile.in
+
+libdir = @plugindir@
+
+INCLUDES = -I@srcdir@
+AM_CFLAGS = @CFLAGS@ @EXTRA_CFLAGS@
+
+lib_LTLIBRARIES = sasl-plain.la
+
+sasl_plain_la_SOURCES = client-plain.c
+sasl_plain_la_LDFLAGS = -module -avoid-version
diff --git a/plain/Makefile.in b/plain/Makefile.in
new file mode 100644
index 0000000..b5178c0
--- /dev/null
+++ b/plain/Makefile.in
@@ -0,0 +1,476 @@
+# Makefile.in generated by automake 1.9.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+SOURCES = $(sasl_plain_la_SOURCES)
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = plain
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
+am__installdirs = "$(DESTDIR)$(libdir)"
+libLTLIBRARIES_INSTALL = $(INSTALL)
+LTLIBRARIES = $(lib_LTLIBRARIES)
+sasl_plain_la_LIBADD =
+am_sasl_plain_la_OBJECTS = client-plain.lo
+sasl_plain_la_OBJECTS = $(am_sasl_plain_la_OBJECTS)
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(sasl_plain_la_SOURCES)
+DIST_SOURCES = $(sasl_plain_la_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CRAMMD5_LIBS = @CRAMMD5_LIBS@
+CRAMMD5_LTLIBOBJS = @CRAMMD5_LTLIBOBJS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DIST_PLUGINS = @DIST_PLUGINS@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+EXTRA_CFLAGS = @EXTRA_CFLAGS@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBESMTP_VERSION = @LIBESMTP_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBTOOL_DEPS = @LIBTOOL_DEPS@
+LIB_AGE = @LIB_AGE@
+LIB_CURRENT = @LIB_CURRENT@
+LIB_REVISION = @LIB_REVISION@
+LN_S = @LN_S@
+LTALLOCA = @LTALLOCA@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+NTLM_LIBS = @NTLM_LIBS@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LDFLAGS = @PTHREAD_LDFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+RANLIB = @RANLIB@
+RPM_BUILDREQUIRES = @RPM_BUILDREQUIRES@
+RPM_OPENSSL = @RPM_OPENSSL@
+RPM_OPENSSLDEVEL = @RPM_OPENSSLDEVEL@
+RPM_REQUIRES = @RPM_REQUIRES@
+SASL_PLUGINS = @SASL_PLUGINS@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+ac_ct_RANLIB = @ac_ct_RANLIB@
+ac_ct_STRIP = @ac_ct_STRIP@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
+am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @plugindir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+plugindir = @plugindir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+subdirs = @subdirs@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+INCLUDES = -I@srcdir@
+AM_CFLAGS = @CFLAGS@ @EXTRA_CFLAGS@
+lib_LTLIBRARIES = sasl-plain.la
+sasl_plain_la_SOURCES = client-plain.c
+sasl_plain_la_LDFLAGS = -module -avoid-version
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plain/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plain/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(libdir)" || $(mkdir_p) "$(DESTDIR)$(libdir)"
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ if test -f $$p; then \
+ f=$(am__strip_dir) \
+ echo " $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(libdir)/$$f"; \
+ else :; fi; \
+ done
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @set -x; list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ p=$(am__strip_dir) \
+ echo " $(LIBTOOL) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$p'"; \
+ $(LIBTOOL) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$p"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+sasl-plain.la: $(sasl_plain_la_OBJECTS) $(sasl_plain_la_DEPENDENCIES)
+ $(LINK) -rpath $(libdir) $(sasl_plain_la_LDFLAGS) $(sasl_plain_la_OBJECTS) $(sasl_plain_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client-plain.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ if $(LTCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+distclean-libtool:
+ -rm -f libtool
+uninstall-info-am:
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+ list='$(DISTFILES)'; for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+ esac; \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkdir_p) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)"; do \
+ test -z "$$dir" || $(mkdir_p) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-info-am uninstall-libLTLIBRARIES
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libLTLIBRARIES clean-libtool ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-exec \
+ install-exec-am install-info install-info-am \
+ install-libLTLIBRARIES install-man install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags uninstall uninstall-am uninstall-info-am \
+ uninstall-libLTLIBRARIES
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plain/client-plain.c b/plain/client-plain.c
new file mode 100644
index 0000000..7891b9e
--- /dev/null
+++ b/plain/client-plain.c
@@ -0,0 +1,113 @@
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "auth-client.h"
+#include "auth-plugin.h"
+
+#define NELT(x) (sizeof x / sizeof x[0])
+
+static int plain_init (void *pctx);
+static void plain_destroy (void *ctx);
+static const char *plain_response (void *ctx, const char *challenge, int *len,
+ auth_interact_t interact, void *arg);
+
+const struct auth_client_plugin sasl_client =
+ {
+ /* Plugin information */
+ "PLAIN",
+ "PLAIN mechanism (RFC 2595 section 6)",
+ /* Plugin instance */
+ plain_init,
+ plain_destroy,
+ /* Authentication */
+ plain_response,
+ AUTH_PLUGIN_PLAIN,
+ /* Security Layer */
+ 0,
+ NULL,
+ NULL,
+ };
+
+static const struct auth_client_request client_request[] =
+ {
+ { "user", AUTH_CLEARTEXT | AUTH_USER, "User Name", 255, },
+ { "passphrase", AUTH_CLEARTEXT | AUTH_PASS, "Pass Phrase", 255, },
+ };
+
+struct plain_context
+ {
+ int state;
+ char buf[2 * 255 + 3];
+ };
+
+static int
+plain_init (void *pctx)
+{
+ struct plain_context *plain;
+
+ plain = malloc (sizeof (struct plain_context));
+ memset (plain, 0, sizeof (struct plain_context));
+
+ *(void **) pctx = plain;
+ return 1;
+}
+
+static void
+plain_destroy (void *ctx)
+{
+ struct plain_context *plain = ctx;
+
+ memset (plain->buf, '\0', sizeof plain->buf);
+ free (plain);
+}
+
+static const char *
+plain_response (void *ctx, const char *challenge __attribute__ ((unused)),
+ int *len, auth_interact_t interact, void *arg)
+{
+ struct plain_context *plain = ctx;
+ char *result[NELT (client_request)];
+
+ switch (plain->state)
+ {
+ case 0:
+ if (!(*interact) (client_request, result, NELT (client_request), arg))
+ break;
+ strcpy (plain->buf + 1, result[0]);
+ strcpy (plain->buf + strlen (result[0]) + 2, result[1]);
+ *len = strlen (result[0]) + strlen (result[1]) + 2;
+ plain->state = -1;
+ return plain->buf;
+ }
+ *len = 0;
+ return NULL;
+}
+
diff --git a/protocol-states.h b/protocol-states.h
new file mode 100644
index 0000000..5550115
--- /dev/null
+++ b/protocol-states.h
@@ -0,0 +1,51 @@
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* To add a new command or state to the SMTP protocol engine, give it
+ a name in this file and write the functions to handle the state. */
+
+S (greeting)
+S (ehlo)
+S (helo)
+#ifdef USE_TLS
+S (starttls)
+#endif
+S (auth)
+S (auth2)
+#ifdef USE_ETRN
+S (etrn)
+#endif
+#ifdef USE_XUSR
+S (xusr)
+#endif
+S (mail)
+S (rcpt)
+S (data)
+S (data2)
+#ifdef USE_CHUNKING
+S (bdat)
+S (bdat2)
+#endif
+S (rset)
+S (quit)
+
+#undef S
diff --git a/protocol.c b/protocol.c
new file mode 100644
index 0000000..0b44df8
--- /dev/null
+++ b/protocol.c
@@ -0,0 +1,1570 @@
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* The SMTP protocol engine and handler functions for the core SMTP
+ commands and their extended parameters. Extended commands are mostly
+ in files of their own. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <missing.h> /* declarations for missing library functions */
+
+#include <sys/socket.h>
+#if HAVE_LWRES_NETDB_H
+# include <lwres/netdb.h>
+#elif !HAVE_GETADDRINFO
+# include "getaddrinfo.h"
+#else
+# include <netdb.h>
+#endif
+
+#if HAVE_UNAME
+#include <sys/utsname.h>
+#endif
+
+#include "libesmtp-private.h"
+#include "message-source.h"
+#include "siobuf.h"
+#include "tokens.h"
+#include "headers.h"
+#include "protocol.h"
+
+struct protocol_states
+ {
+ void (*cmd) (siobuf_t conn, smtp_session_t session);
+ void (*rsp) (siobuf_t conn, smtp_session_t session);
+ };
+
+/* The following array of state handlers is indexed by the state (!) */
+struct protocol_states protocol_states[] =
+ {
+#define S(x) { cmd_##x, rsp_##x, },
+#include "protocol-states.h"
+ };
+
+static int
+set_first_recipient (smtp_session_t session)
+{
+ smtp_recipient_t recipient;
+
+ if (session->current_message == NULL)
+ return 0;
+
+ for (recipient = session->current_message->recipients;
+ recipient != NULL;
+ recipient = recipient->next)
+ if (!recipient->complete)
+ break;
+ session->cmd_recipient = session->rsp_recipient = recipient;
+ return recipient != NULL;
+}
+
+/* Return a pointer to the next unsent recipient. This can't operate
+ on the session structure directly as the other first/next functions
+ do since there are two variables for the current recipient due to
+ the implementation of PIPELINING. */
+static smtp_recipient_t
+next_recipient (smtp_recipient_t recipient)
+{
+ while ((recipient = recipient->next) != NULL)
+ if (!recipient->complete)
+ break;
+ return recipient;
+}
+
+/* Set the session's current message to the next unsent message.
+ */
+int
+next_message (smtp_session_t session)
+{
+ while ((session->current_message = session->current_message->next) != NULL)
+ if (set_first_recipient (session))
+ return 1;
+ return 0;
+}
+
+/* Set the current message to the first unsent message in the
+ session. */
+static int
+set_first_message (smtp_session_t session)
+{
+ for (session->current_message = session->messages;
+ session->current_message != NULL;
+ session->current_message = session->current_message->next)
+ if (set_first_recipient (session))
+ return 1;
+ return 0;
+}
+
+/*****************************************************************************
+ * The main protocol engine.
+ *****************************************************************************/
+
+int
+do_session (smtp_session_t session)
+{
+ struct addrinfo hints, *res, *addrs;
+ int err;
+ int sd;
+ siobuf_t conn;
+ int nresp, status, want_flush, fast;
+ char *nodename;
+
+#if HAVE_UNAME
+ if (session->localhost == NULL)
+ {
+ struct utsname name;
+
+ if (uname (&name) < 0)
+ {
+ set_errno (errno);
+ return 0;
+ }
+ session->localhost = strdup (name.nodename);
+ if (session->localhost == NULL)
+ {
+ set_errno (ENOMEM);
+ return 0;
+ }
+ }
+#elif HAVE_GETHOSTNAME
+ if (session->localhost == NULL)
+ {
+ char host[256];
+
+ if (gethostname (host, sizeof host) < 0)
+ {
+ set_errno (errno);
+ return 0;
+ }
+ session->localhost = strdup (host);
+ if (session->localhost == NULL)
+ {
+ set_errno (ENOMEM);
+ return 0;
+ }
+ }
+#endif
+
+ /* Initialise the current message and recipient variables in the
+ session. This returns zero if there is no work to do. */
+#ifndef USE_ETRN
+ if (!set_first_message (session))
+#else
+ if (!set_first_message (session) && session->etrn_nodes == NULL)
+#endif
+ {
+ set_error (SMTP_ERR_NOTHING_TO_DO);
+ return 0;
+ }
+
+ /* Create a message source only if it is needed.
+ */
+ if (session->msg_source == NULL && session->current_message != NULL)
+ {
+ session->msg_source = msg_source_create ();
+ if (session->msg_source == NULL)
+ {
+ set_errno (ENOMEM);
+ return 0;
+ }
+ }
+
+ /* Connect to the SMTP server. The following code will only work for
+ socket connections at present. This will eventually change to
+ permit connections on any type of file descriptor, e.g. for LMTP
+ servers or forking an SMTP server which can run the protocol on
+ its standard input. */
+
+ errno = 0;
+ nodename = (session->host == NULL || *session->host == '\0') ? NULL
+ : session->host;
+ /* Use the RFC 2553/Posix resolver interface. This allows for much
+ cleaner code, protocol independence and thread safety. */
+ memset (&hints, 0, sizeof hints);
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ err = getaddrinfo (nodename, session->port, &hints, &res);
+ if (err != 0)
+ {
+ set_herror (err);
+ return 0;
+ }
+
+ /* Try to establish an SMTP session with each host in turn until one
+ succeeds. */
+ for (addrs = res; addrs != NULL; addrs = addrs->ai_next)
+ {
+ sd = socket (addrs->ai_family, addrs->ai_socktype, addrs->ai_protocol);
+ if (sd < 0)
+ {
+ set_errno (errno);
+ continue;
+ }
+ if (connect (sd, addrs->ai_addr, addrs->ai_addrlen) < 0)
+ {
+ /* Failed to connect. Close the socket and try again. */
+ set_errno (errno);
+ close (sd);
+ continue;
+ }
+
+ /* Add buffering to the socket */
+ conn = sio_attach (sd, sd, SIO_BUFSIZE);
+ if (conn == NULL)
+ {
+ set_errno (ENOMEM);
+ freeaddrinfo (res);
+ close (sd);
+ return 0;
+ }
+
+ /* If monitoring the protocol, pass the callback on to the sio_
+ package. */
+ if (session->monitor_cb != NULL)
+ sio_set_monitorcb (conn, session->monitor_cb, session->monitor_cb_arg);
+
+ if (session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_CONNECT, session->event_cb_arg);
+
+ /* Outer loop of the protocol. This is trickier than superficial
+ examination of RFC 821 would suggest, however the complexity is
+ required to handle batched commands and responses. RFC 821 makes
+ no comment on the underlying transport so it cannot be assumed that
+ each command and its response is transported in a single packet on
+ the network or that the network preserves packet boundaries (or,
+ for that matter, that the underlying transport is even a network,
+ e.g. it might be a pipe or a AF_UNIX socket). Multiple commands
+ may be sent in a single packet and similarly multiple responses
+ may be returned in a single packet. RFC 2920 which describes the
+ PIPELINING SMTP extension clarifies this for the case where the
+ underlying transport is TCP/IP.
+
+ In this implementation the outer loop will batch together as many
+ commands as possible until the remote server sends a response (this
+ will normally happen after the local transmit buffer is flushed)
+ or a command has been sent which requires a response before the
+ client can proceed.
+
+ In order not to break certain SMTP server implementations, flushing
+ is done after every command unless the PIPELINING keyword is received
+ in response to the EHLO command. Even when PIPELINING is in force
+ certain commands *always* flush the transmit buffer.
+
+ One good reason for wanting PIPELINING is that when submitting mail
+ to an ISP's sluggish server, there will be a big performance boost
+ since many round trips are eliminated.
+
+ As the protocol engine advances through its states, it will walk
+ through the messages and recipients within the session.
+
+ The protocol functions set a few timeouts as they progress. The
+ values set are those reccommended in RFC 2821.
+
+ [is it just me, or does everybody find that it's easier to implement
+ protocols on the server side?]
+ */
+
+ /* Reset variables to their initial state before entering the protocol
+ main loop. */
+ session->extensions = 0;
+ session->try_fallback_server = 0;
+ reset_status (&session->mta_status);
+ destroy_auth_mechanisms (session);
+ session->authenticated = 0;
+#ifdef USE_TLS
+ session->using_tls = 0;
+#endif
+
+ nresp = 0;
+ session->cmd_state = session->rsp_state = 0;
+ while (session->rsp_state >= 0)
+ {
+ if (session->cmd_state == -1)
+ session->cmd_state = session->rsp_state;
+ (*protocol_states[session->cmd_state].cmd) (conn, session);
+ sio_mark (conn);
+ if (!(session->extensions & EXT_PIPELINING))
+ session->cmd_state = -1;
+ nresp++;
+
+ if (session->rsp_state < 0)
+ break;
+
+ /* The following loop polls the server and reads or writes
+ to it as required.
+
+ When the command state is set to -1, this signals that no
+ more commands can be issued until the response to the most
+ recent command has been processed, therefore the write
+ buffer must be explicitly flushed. If the command state is
+ not -1, more commands may be issued however, pending
+ responses from the server should be processed. When this
+ is the case, sio_poll should return immediately if there is
+ nothing to read. `fast' requests this non-blocking poll.
+
+ `want_flush' indicates that the write buffer should be
+ sent to the server. This flag remains set until the buffer
+ has been written.
+
+ After explicitly flushing the buffer, sio_poll blocks
+ waiting to read data from the server since the server may
+ take some time to complete the pending commands.
+ */
+ want_flush = (session->cmd_state == -1);
+ fast = (session->cmd_state != -1);
+ while ((status = sio_poll (conn, nresp > 0, want_flush, fast)) > 0)
+ {
+ if (status & SIO_READ)
+ {
+ nresp--;
+
+ /* TODO: change so that the response line is parsed
+ here. This means that the server 421
+ response which can be issued at any time may
+ be checked for here. */
+
+ /* When reading from the server in the response state
+ handlers the read call blocks. Complete responses
+ must be read from the server before processing and
+ an individual response may be larger than the read
+ buffer. */
+ (*protocol_states[session->rsp_state].rsp) (conn, session);
+ }
+ /* XXX - Here I assume that once the write fd becomes
+ available for writing, it stays that way until
+ it is written to. I.e. a blocking read() or a
+ subsequent poll() will not revoke the writable
+ status. Could somebody confirm that this is the
+ case? */
+ if (status & SIO_WRITE)
+ {
+ sio_flush (conn);
+ want_flush = 0;
+ }
+ }
+ if (status < 0)
+ {
+ set_error (SMTP_ERR_DROPPED_CONNECTION);
+ break;
+ }
+ }
+
+ sio_detach (conn);
+ close (sd);
+
+ if (session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_DISCONNECT,
+ session->event_cb_arg);
+
+ /* This flag will be set if the server was reached OK but was the
+ wrong kind of server or the client is told to go away. So if
+ not set the protocol must have concluded sucessfully. */
+ if (!session->try_fallback_server)
+ {
+ freeaddrinfo (res);
+ return 1;
+ }
+ }
+
+ /* If the loop terminated, couldn't work with any servers. */
+ freeaddrinfo (res);
+ return 0;
+}
+
+/*****************************************************************************
+ * Response parser.
+ *****************************************************************************/
+
+static int
+parse_status_triplet (char *p, char **ep, struct smtp_status *triplet)
+{
+ triplet->enh_class = strtol (p, &p, 10);
+ if (*p++ != '.')
+ return 0;
+ triplet->enh_subject = strtol (p, &p, 10);
+ if (*p++ != '.')
+ return 0;
+ triplet->enh_detail = strtol (p, &p, 10);
+ *ep = p;
+ return 1;
+}
+
+static int
+compare_status_triplet (struct smtp_status *a, struct smtp_status *b)
+{
+ return a->enh_class == b->enh_class
+ && a->enh_subject == b->enh_subject
+ && a->enh_detail == b->enh_detail;
+}
+
+/* Free memory allocated in read_smtp_response()
+ and clear the status structure */
+void
+reset_status (struct smtp_status *status)
+{
+ if (status->text != NULL)
+ free ((void *) status->text);
+ memset (status, 0, sizeof (struct smtp_status));
+}
+
+/* All SMTP responses have standard syntax. This function could be
+ called by the protocol engine above and the results from the parsed
+ response passed to the response handler functions. However certain
+ commands involve extra intermediate exchanges with the server that
+ may not correspond to the standard syntax. The response handlers
+ must therefore call this function themselves. */
+int
+read_smtp_response (siobuf_t conn, smtp_session_t session,
+ struct smtp_status *status,
+ int (*cb) (smtp_session_t, char *))
+{
+ struct catbuf text;
+ char buf[1024];
+ char *p, *nul;
+ int code, more, want_enhanced, textlen;
+ struct smtp_status triplet;
+
+ /* First line of an SMTP response is the normal one. Put it in a buffer.
+ Save the status code and the enhanced status triplet if ENHSTATUSCODES
+ is set. The remainder of the line is the message from the server.
+ If there are any continuation lines, get the status code and enhanced
+ status triplet and check that they are the same as the first response
+ line. If not there is a protocol error. Process the extra lines
+ by calling a callback. If not supplied, concatenate the lines
+ (excluding the status info) with the first line. */
+
+ reset_status (status);
+ if ((p = sio_gets (conn, buf, sizeof buf)) == NULL)
+ {
+ set_error (SMTP_ERR_DROPPED_CONNECTION);
+ return -1;
+ }
+ status->code = strtol (p, &p, 10);
+ if (!(*p == ' ' || *p == '-'))
+ {
+ set_error (SMTP_ERR_INVALID_RESPONSE_SYNTAX);
+ return -1;
+ }
+ more = *p++ == '-';
+
+ /* RFC 2034 states that only 2xx, 4xx and 5xx responses are accompanied
+ by enhanced status codes. */
+ code = status->code / 100;
+ want_enhanced = (session->extensions & EXT_ENHANCEDSTATUSCODES);
+ if (code != 2 && code != 4 && code != 5)
+ want_enhanced = 0;
+
+ if (want_enhanced && !parse_status_triplet (p, &p, status))
+ {
+ set_error (SMTP_ERR_INVALID_RESPONSE_SYNTAX);
+ return -1;
+ }
+ while (isspace (*p))
+ p++;
+
+ /* p points to the remainder of the line. This is the text of the
+ server message */
+ cat_init (&text, 128);
+ concatenate (&text, p, -1);
+
+ while (more)
+ {
+ if ((p = sio_gets (conn, buf, sizeof buf)) == NULL)
+ {
+ cat_free (&text);
+ set_error (SMTP_ERR_DROPPED_CONNECTION);
+ return -1;
+ }
+ code = strtol (p, &p, 10);
+ if (code != status->code)
+ {
+ cat_free (&text);
+ set_error (SMTP_ERR_STATUS_MISMATCH);
+ return -1;
+ }
+ if (!(*p == ' ' || *p == '-'))
+ {
+ cat_free (&text);
+ set_error (SMTP_ERR_INVALID_RESPONSE_SYNTAX);
+ return -1;
+ }
+ more = *p++ == '-';
+ if (want_enhanced)
+ {
+ if (!parse_status_triplet (p, &p, &triplet))
+ {
+ cat_free (&text);
+ set_error (SMTP_ERR_INVALID_RESPONSE_SYNTAX);
+ return -1;
+ }
+ if (!compare_status_triplet (status, &triplet))
+ {
+ cat_free (&text);
+ set_error (SMTP_ERR_STATUS_MISMATCH);
+ return -1;
+ }
+ }
+
+ /* Skip whitespace but don't wander over the CRLF */
+ while (isspace (*p) && isprint (*p))
+ p++;
+
+ /* Check that the line is correctly terminated. */
+ nul = strchr (p, '\0');
+ if (nul == NULL || nul == p || nul[-1] != '\n')
+ {
+ cat_free (&text);
+ set_error (SMTP_ERR_UNTERMINATED_RESPONSE);
+ return -1;
+ }
+
+ /* `p' points to the remainder of the line. Either process with the
+ callback or concatenate with the first line. */
+ if (cb != NULL)
+ (*cb) (session, p);
+ else
+ concatenate (&text, p, nul - p);
+
+ /* Check if the total text returned in a multiline response
+ exceeds 4k. Abort if this happens, this might be a DoS attack
+ or a broken server. */
+ textlen = 0;
+ cat_buffer (&text, &textlen);
+ if (textlen > 4096)
+ {
+ cat_free (&text);
+ set_error (SMTP_ERR_UNTERMINATED_RESPONSE);
+ return -1;
+ }
+ }
+
+ /* Terminate and save the response text */
+ concatenate (&text, "", 1);
+ status->text = cat_shrink (&text, NULL);
+
+ return status->code / 100;
+}
+
+/*****************************************************************************
+ * Command and response handlers. Return value is the next send/response
+ * state. If a cmd_xxxx() function returns -1, the response determines
+ * the next state, implying that a flush is needed.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Server Greeting
+ *****************************************************************************/
+
+/* If the response to the server greeting is not a 2xx status code,
+ issue the QUIT command and terminate the session. */
+void
+cmd_greeting (siobuf_t conn, smtp_session_t session)
+{
+ /* Set a five minute timeout. */
+ sio_set_timeout (conn, session->greeting_timeout);
+ session->cmd_state = -1;
+}
+
+void
+rsp_greeting (siobuf_t conn, smtp_session_t session)
+{
+ int code;
+
+ code = read_smtp_response (conn, session, &session->mta_status, NULL);
+ if (code == 2 && session->mta_status.code == 220)
+ session->rsp_state = S_ehlo;
+ else if (code == 4 || code == 5)
+ {
+ session->rsp_state = S_quit; /* Graceful exit using QUIT */
+ session->try_fallback_server = 1;
+ }
+ else
+ {
+ session->rsp_state = -1; /* Drop the connection and run */
+ session->try_fallback_server = 1;
+ }
+
+ /* TODO: certain broken servers may break when the EHLO command is
+ issued. For example, one known behaviour is to close the
+ connection after returning the 501 response. This is wrong
+ but it happens.
+
+ An API option is needed to avoid using EHLO!
+ */
+}
+
+/*****************************************************************************
+ * EHLO
+ *****************************************************************************/
+
+/* EHLO is the preferred client greeting to the server. The parameter is
+ the FQDN of the client host. The server response is the greeting line
+ followed by extra lines listing the server capabilities. The additional
+ lines are parsed and set the session capability flags. If the response
+ is "501 command not implemented", the HELO command should be used instead.
+ Since the next command issued depends on the server response the output
+ must be flushed.
+
+ Next state is one of Auth, Helo, Mail.
+ */
+void
+cmd_ehlo (siobuf_t conn, smtp_session_t session)
+{
+ sio_printf (conn, "EHLO %s\r\n", session->localhost);
+ session->cmd_state = -1;
+}
+
+#define no_required_extension(s,e) \
+ (((s)->required_extensions & (e)) && !((s)->extensions & (e)))
+
+static int
+report_extensions (smtp_session_t session)
+{
+ int quit_now;
+ unsigned long exts = 0;
+
+ /* Report extensions that are required but not available.
+ */
+ if (no_required_extension (session, EXT_DSN))
+ {
+ quit_now = 0;
+ if (session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_EXTNA_DSN,
+ session->event_cb_arg, &quit_now);
+ if (quit_now)
+ exts |= EXT_DSN;
+ }
+#ifdef USE_CHUNKING
+ if (no_required_extension (session, EXT_CHUNKING))
+ {
+ quit_now = 0;
+ if (session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_EXTNA_CHUNKING,
+ session->event_cb_arg, &quit_now);
+ if (quit_now)
+ exts |= EXT_CHUNKING;
+ }
+ if (no_required_extension (session, EXT_BINARYMIME))
+ {
+ if (session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_EXTNA_BINARYMIME,
+ session->event_cb_arg);
+ exts |= EXT_BINARYMIME;
+ }
+#endif
+ if (no_required_extension (session, EXT_8BITMIME))
+ {
+ if (session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_EXTNA_8BITMIME,
+ session->event_cb_arg);
+ exts |= EXT_8BITMIME;
+ }
+#ifdef USE_ETRN
+ if (no_required_extension (session, EXT_ETRN))
+ {
+ quit_now = 1;
+ if (session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_EXTNA_ETRN,
+ session->event_cb_arg, &quit_now);
+ if (quit_now)
+ exts |= EXT_ETRN;
+ }
+#endif
+ return !exts;
+}
+
+static int
+cb_ehlo (smtp_session_t session, char *buf)
+{
+ const char *p;
+ char token[32];
+
+ if (!read_atom (skipblank (buf), &p, token, sizeof token))
+ {
+ /* expecting an atom - do nothing */
+ return 0;
+ }
+
+ /* Since the session structure mostly just carries a bit for each of
+ the extensions, there is no point #ifdefing out the extension
+ keywords for omitted features. Also extensions may be added
+ in the future so it is not an error to have an unrecognised
+ extension keyword. */
+
+ if (strcasecmp (token, "ENHANCEDSTATUSCODES") == 0) /* RFC 1893, RFC 2034 */
+ session->extensions |= EXT_ENHANCEDSTATUSCODES;
+ else if (strcasecmp (token, "PIPELINING") == 0) /* RFC 2920 */
+ session->extensions |= EXT_PIPELINING;
+ else if (strcasecmp (token, "DSN") == 0) /* RFC 1891 */
+ session->extensions |= EXT_DSN;
+ else if (strcasecmp (token, "AUTH") == 0) /* RFC 2554 */
+ {
+ session->extensions |= EXT_AUTH;
+ set_auth_mechanisms (session, p);
+ }
+#ifdef AUTH_ID_HACK
+ else if (strncasecmp (token, "AUTH=", 5) == 0) /* non-standard syntax */
+ {
+ session->extensions |= EXT_AUTH;
+ set_auth_mechanisms (session, token + 5);
+ set_auth_mechanisms (session, p);
+ }
+#endif
+ else if (strcasecmp (token, "STARTTLS") == 0) /* RFC 3207 */
+ session->extensions |= EXT_STARTTLS;
+ else if (strcasecmp (token, "SIZE") == 0) /* RFC 1870 */
+ {
+ session->extensions |= EXT_SIZE;
+ session->size_limit = strtol (p, NULL, 10);
+ }
+ else if (strcasecmp (token, "CHUNKING") == 0) /* RFC 3030 */
+ session->extensions |= EXT_CHUNKING;
+ else if (strcasecmp (token, "BINARYMIME") == 0) /* RFC 3030 */
+ session->extensions |= EXT_BINARYMIME;
+ else if (strcasecmp (token, "8BITMIME") == 0) /* RFC 1652 */
+ session->extensions |= EXT_8BITMIME;
+ else if (strcasecmp (token, "DELIVERBY") == 0) /* RFC 2852 */
+ {
+ session->extensions |= EXT_DELIVERBY;
+ session->min_by_time = strtol (p, NULL, 10);
+ }
+ else if (strcasecmp (token, "ETRN") == 0) /* RFC 1985 */
+ session->extensions |= EXT_ETRN;
+ else if (strcasecmp (token, "XUSR") == 0) /* sendmail (I feel ill) */
+ session->extensions |= EXT_XUSR;
+ else if (strcasecmp (token, "XEXCH50") == 0) /* exchange (I feel worse) */
+ session->extensions |= EXT_XEXCH50;
+ return 1;
+}
+
+
+void
+rsp_ehlo (siobuf_t conn, smtp_session_t session)
+{
+ int code;
+
+ session->extensions = 0;
+ destroy_auth_mechanisms (session);
+ code = read_smtp_response (conn, session, &session->mta_status, cb_ehlo);
+ if (code < 0)
+ {
+ session->rsp_state = S_quit;
+ return;
+ }
+
+ if (code != 2)
+ session->extensions = 0;
+
+ if (code == 4)
+ {
+ /* 4xx failure code. Something is temporarily wrong. Fail the
+ entire session and let the application retry later. */
+ session->rsp_state = S_quit;
+ session->try_fallback_server = 1;
+ return;
+ }
+ else if (code == 5)
+ {
+ /* 5xx failure code. Something is permanently wrong. There are
+ a number of codes indicating that HELO is worth a try since
+ the server did not understand EHLO. Otherwise fail the entire
+ session. The application must correct something and retry later. */
+ if (session->mta_status.code == 500 || session->mta_status.code == 501
+ || session->mta_status.code == 502 || session->mta_status.code == 504)
+ session->rsp_state = S_helo;
+ else
+ session->rsp_state = S_quit;
+ return;
+ }
+ else if (code != 2)
+ {
+ /* Response must be 2xx, 4xx or 5xx */
+ set_error (SMTP_ERR_INVALID_RESPONSE_STATUS);
+ session->rsp_state = S_quit;
+ return;
+ }
+
+#ifdef USE_TLS
+ /* Totally ignore the TLS stuff if it's already in use */
+ if (!session->using_tls && session->starttls_enabled != Starttls_DISABLED)
+ {
+ if (select_starttls (session))
+ {
+ session->rsp_state = S_starttls;
+ return;
+ }
+ if (session->starttls_enabled == Starttls_REQUIRED)
+ {
+ if (session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_EXTNA_STARTTLS,
+ session->event_cb_arg, NULL);
+ session->rsp_state = S_quit;
+ set_error (SMTP_ERR_EXTENSION_NOT_AVAILABLE);
+ return;
+ }
+ }
+#endif
+ /* If AUTH is enabled but no mechanisms can be selected, move on to the
+ MAIL command since the MTA is required to accept mail for its own
+ domain. */
+ if ((session->extensions & EXT_AUTH) && select_auth_mechanism (session))
+ {
+ session->rsp_state = S_auth;
+ return;
+ }
+
+ /* Report extensions *after* starting TLS or doing AUTH since either
+ of these can restart the session with different SMTP extensions
+ being offered by the server. */
+ if (!report_extensions (session))
+ {
+ set_error (SMTP_ERR_EXTENSION_NOT_AVAILABLE);
+ session->rsp_state = S_quit;
+ return;
+ }
+
+#ifdef USE_ETRN
+ session->rsp_state = check_etrn (session)
+ ? S_etrn : initial_transaction_state (session);
+#else
+ session->rsp_state = initial_transaction_state (session);
+#endif
+}
+
+/* Select the correct initial state after reading the EHLO response or
+ after DATA or RSET in the previous transaction. */
+int
+initial_transaction_state (smtp_session_t session)
+{
+#ifdef USE_XUSR
+ if (session->extensions & EXT_XUSR)
+ return S_xusr;
+#endif
+ return S_mail;
+}
+
+/*****************************************************************************
+ * HELO
+ *****************************************************************************/
+
+/* HELO is absolutely *not* the preferred client greeting to the server.
+ The parameter is the FQDN of the client host. The server response is
+ the greeting line. No capabilities are reported so all extensions
+ are turned off.
+
+ Next state is Mail.
+ */
+void
+cmd_helo (siobuf_t conn, smtp_session_t session)
+{
+ sio_printf (conn, "HELO %s\r\n", session->localhost);
+ session->cmd_state = -1;
+}
+
+void
+rsp_helo (siobuf_t conn, smtp_session_t session)
+{
+ int code;
+#ifdef USE_TLS
+ int notls;
+#endif
+
+ session->extensions = 0;
+ destroy_auth_mechanisms (session);
+ code = read_smtp_response (conn, session, &session->mta_status, NULL);
+ if (code < 0)
+ {
+ session->try_fallback_server = 1;
+ session->rsp_state = S_quit;
+ return;
+ }
+ if (code != 2)
+ {
+ if (code != 4 || code != 5)
+ set_error (SMTP_ERR_INVALID_RESPONSE_STATUS);
+ session->try_fallback_server = 1;
+ session->rsp_state = S_quit;
+ return;
+ }
+
+#ifdef USE_TLS
+ /* Care must be taken when checking for required TLS. The client may
+ have connected to a tunnel which offers the STARTTLS extension and
+ the real server does not implement SMTP extensions. Hence the
+ check that TLS is actually in use before reporting that the
+ extension is not available. */
+ notls = !session->using_tls && session->starttls_enabled == Starttls_REQUIRED;
+ if (notls && session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_EXTNA_STARTTLS,
+ session->event_cb_arg, NULL);
+#else
+# define notls 0
+#endif
+
+ /* There are no extensions. Make sure none were required. */
+ if (!report_extensions (session) || notls)
+ {
+ set_error (SMTP_ERR_EXTENSION_NOT_AVAILABLE);
+ session->rsp_state = S_quit;
+ return;
+ }
+
+ /* Unlike EHLO, the only next state can be Mail, since there are
+ no extensions to check for message acceptability or options to set
+ before proceeding. */
+ session->rsp_state = initial_transaction_state (session);
+}
+
+/*****************************************************************************
+ * MAIL FROM:
+ *****************************************************************************/
+
+/* MAIL FROM: is the first step in sending a message. Select the first
+ or a subsequent message from the session structure. The message sender
+ is taken from the message structure.
+
+ Next state is always Rcpt, therefore this command need not be flushed.
+ */
+void
+cmd_mail (siobuf_t conn, smtp_session_t session)
+{
+ const char *mailbox;
+ smtp_message_t message;
+ char xtext[256];
+
+ /* Set a five minute timeout. This stays in force until the DATA
+ command. */
+ sio_set_timeout (conn, session->envelope_timeout);
+
+ message = session->current_message;
+ mailbox = message->reverse_path_mailbox;
+ sio_printf (conn, "MAIL FROM:<%s>", (mailbox != NULL) ? mailbox : "");
+
+ /* SIZE: SIZE=message-size-estimate */
+ if ((session->extensions & EXT_SIZE) && message->size_estimate > 0)
+ sio_printf (conn, " SIZE=%lu", message->size_estimate);
+
+ /* DSN: RET=FULL/HDRS ENVID=xtext */
+ if (session->extensions & EXT_DSN)
+ {
+ static const char *ret[] = { NULL, "FULL", "HDRS" };
+
+ if (message->dsn_ret != Ret_NOTSET)
+ sio_printf (conn, " RET=%s", ret[message->dsn_ret]);
+
+ if (message->dsn_envid != NULL)
+ sio_printf (conn, " ENVID=%s",
+ encode_xtext (xtext, sizeof xtext, message->dsn_envid));
+ }
+
+ /* 8BITMIME: BODY=7BIT/8BITMIME/BINARYMIME */
+#ifdef USE_CHUNKING
+ if ((session->extensions & (EXT_8BITMIME | EXT_BINARYMIME))
+#else
+ if ((session->extensions & EXT_8BITMIME)
+#endif
+ && message->e8bitmime != E8bitmime_NOTSET)
+ {
+ sio_write (conn, " BODY=", -1);
+ if (message->e8bitmime == E8bitmime_8BITMIME)
+ sio_write (conn, "8BITMIME", -1);
+ else if (message->e8bitmime == E8bitmime_7BIT)
+ sio_write (conn, "7BIT", -1);
+#ifdef USE_CHUNKING
+ else if (message->e8bitmime == E8bitmime_BINARYMIME)
+ sio_write (conn, "BINARYMIME", -1);
+#endif
+ }
+
+ if ((session->extensions & EXT_DELIVERBY) && message->by_mode != By_NOTSET)
+ {
+ static char mode[] = { 'N', 'R', };
+ long by_time;
+
+ by_time = message->by_time;
+ /* If the by_time is greater than the server's min_by_time, ask the
+ application what to do. If adjust is set > 0, the message's
+ deliver by time is adjusted to be acceptable to the server.
+ If not, the MAIL command will be failed by the server. */
+ if (session->min_by_time > 0 && by_time < session->min_by_time)
+ {
+ int adjust = 0;
+
+ if (session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_DELIVERBY_EXPIRED,
+ session->event_cb_arg,
+ session->min_by_time - by_time, &adjust);
+ if (adjust > 0)
+ by_time = session->min_by_time + adjust;
+ }
+ sio_printf (conn, " BY=%ld%c%s", by_time,
+ mode[message->by_mode], (message->by_trace) ? "T" : "");
+ }
+
+ sio_write (conn, "\r\n", 2);
+ /* TODO: until code to prevent issuing of further RCPT commands and to
+ discard RCPT responses cascading from an error response to
+ MAIL is in place, flush the mail command even when pipelining. */
+#if 0
+ session->cmd_state = S_rcpt;
+#else
+ session->cmd_state = -1;
+#endif
+}
+
+void
+rsp_mail (siobuf_t conn, smtp_session_t session)
+{
+ int code;
+ smtp_message_t message;
+
+ message = session->current_message;
+ code = read_smtp_response (conn, session,
+ &message->reverse_path_status, NULL);
+ if (code < 0)
+ {
+ session->rsp_state = S_quit;
+ return;
+ }
+
+ /* Notify the MAIL FROM: status */
+ if (session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_MAILSTATUS, session->event_cb_arg,
+ message->reverse_path_mailbox, message);
+ if (code != 2)
+ {
+ if (next_message (session))
+ session->rsp_state = initial_transaction_state (session);
+ else
+ session->rsp_state = S_quit;
+ }
+ else
+ {
+ message->valid_recipients = 0;
+ message->failed_recipients = 0;
+ session->rsp_state = S_rcpt;
+ }
+}
+
+/*****************************************************************************
+ * RCPT TO:
+ *****************************************************************************/
+
+/* Specify one message recipient. This is taken from the recipient
+ parameters. Many parameters are possible depending on the extensions
+ enabled. For errors such as unknown recipient, or cannot relay,
+ just mark the offending recipient as failing and continue normally.
+
+ Next state is Rcpt, Data or Bdat. This depends on the number of
+ recipients for the message and the extensions enabled. This selection
+ can be made without waiting for the server response therefore this
+ command need not be flushed.
+ */
+void
+cmd_rcpt (siobuf_t conn, smtp_session_t session)
+{
+ static struct { enum notify_flags mask; const char *flag; } masks[] =
+ {
+ { Notify_SUCCESS, "SUCCESS", },
+ { Notify_FAILURE, "FAILURE", },
+ { Notify_DELAY, "DELAY", },
+ };
+ smtp_recipient_t recipient;
+ enum notify_flags notify;
+ char xtext[256];
+ int i;
+
+ recipient = session->cmd_recipient;
+ sio_printf (conn, "RCPT TO:<%s>", recipient->mailbox);
+
+ if (session->extensions & EXT_DSN)
+ {
+ /* DSN: NOTIFY=NEVER/SUCCESS,FAILURE,DELAY */
+ notify = recipient->dsn_notify;
+ if (notify != Notify_NOTSET)
+ {
+ sio_write (conn, " NOTIFY=", -1);
+ if (notify == Notify_NEVER)
+ sio_write (conn, "NEVER", -1);
+ else
+ for (i = 0; i < (int) (sizeof masks / sizeof masks[0]); i++)
+ if (notify & masks[i].mask)
+ {
+ notify &= ~masks[i].mask;
+ sio_write (conn, masks[i].flag, -1);
+ if (notify != 0)
+ sio_write (conn, ",", 1);
+ }
+ }
+
+ /* DSN: ORCPT=type;address */
+ if (recipient->dsn_orcpt != NULL)
+ sio_printf (conn, " ORCPT=%s;%s", recipient->dsn_addrtype,
+ encode_xtext (xtext, sizeof xtext, recipient->dsn_orcpt));
+ }
+ sio_write (conn, "\r\n", 2);
+
+ session->cmd_recipient = next_recipient (session->cmd_recipient);
+ if (session->cmd_recipient != NULL)
+ session->cmd_state = S_rcpt;
+ else if (session->require_all_recipients)
+ /* can't pipeline the DATA command when require_all_recpients is set. */
+ session->cmd_state = -1;
+ else
+#ifdef USE_CHUNKING
+ session->cmd_state = (session->extensions & EXT_CHUNKING) ? S_bdat : S_data;
+#else
+ session->cmd_state = S_data;
+#endif
+}
+
+void
+rsp_rcpt (siobuf_t conn, smtp_session_t session)
+{
+ int code;
+
+ code = read_smtp_response (conn, session,
+ &session->rsp_recipient->status, NULL);
+ if (code < 0)
+ {
+ session->rsp_state = S_quit;
+ return;
+ }
+
+ if (code == 2)
+ session->current_message->valid_recipients += 1;
+ else
+ session->current_message->failed_recipients += 1;
+
+ /* MTA will never accept this recipient. Make sure it isn't used
+ again. */
+ if (code == 5)
+ session->rsp_recipient->complete = 1;
+
+ /* Notify the RCPT TO: status */
+ if (session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_RCPTSTATUS, session->event_cb_arg,
+ session->rsp_recipient->mailbox,
+ session->rsp_recipient);
+
+ session->rsp_recipient = next_recipient (session->rsp_recipient);
+ if (session->rsp_recipient != NULL)
+ session->rsp_state = S_rcpt;
+ else if (session->require_all_recipients
+ && session->current_message->failed_recipients > 0)
+ {
+ reset_status (&session->current_message->message_status);
+ session->rsp_state = next_message (session) ? S_rset : S_quit;
+ }
+ else
+#ifdef USE_CHUNKING
+ session->rsp_state = (session->extensions & EXT_CHUNKING) ? S_bdat : S_data;
+#else
+ session->rsp_state = S_data;
+#endif
+}
+
+/*****************************************************************************
+ * DATA
+ *****************************************************************************/
+
+void
+cmd_data (siobuf_t conn, smtp_session_t session)
+{
+ sio_set_timeout (conn, session->data_timeout);
+ sio_write (conn, "DATA\r\n", -1);
+ session->cmd_state = -1;
+}
+
+void
+rsp_data (siobuf_t conn, smtp_session_t session)
+{
+ int code;
+ smtp_message_t message;
+
+ message = session->current_message;
+ code = read_smtp_response (conn, session, &message->message_status, NULL);
+ if (code < 0)
+ {
+ session->rsp_state = S_quit;
+ return;
+ }
+ if (code == 4 || code == 5)
+ {
+ /* The server will not accept this message.
+ */
+
+ /* N.B. This is a bit tricky. RFC 821 isn't clear and I can't
+ find any information in RFC 2821 either but what state is the
+ SMTP server supposed to be in when the DATA command fails a)
+ when the 354 response is expected and b) after the message is
+ copied to the server.
+
+ Play safe and issue the RSET command. */
+ if (next_message (session))
+ session->rsp_state = S_rset;
+ else
+ session->rsp_state = S_quit;
+ }
+ else if (code != 3)
+ {
+ set_error (SMTP_ERR_INVALID_RESPONSE_STATUS);
+ session->rsp_state = S_quit;
+ }
+ else
+ session->rsp_state = S_data2;
+
+ /* Notify end of message here if not transferring anything */
+ if (code != 3 && session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_MESSAGESENT,
+ session->event_cb_arg, message);
+}
+
+/* Read the message from the application using the callback.
+ Break into lines and copy to the server. */
+void
+cmd_data2 (siobuf_t conn, smtp_session_t session)
+{
+ const char *line, *header, *pline, *p;
+ int c, len;
+
+ /* RFC 2920 - some servers may return a 354 response to DATA even
+ if there are no valid recipients. If this happens just send a
+ line containing .\r\n to terminate the command. It will then
+ fail as expected. */
+ if (session->current_message->valid_recipients == 0)
+ {
+ sio_write (conn, ".\r\n", 3);
+ session->cmd_state = -1;
+ return;
+ }
+
+ sio_set_timeout (conn, session->transfer_timeout);
+
+ /* Arrange to read the current message from the application. */
+ msg_source_set_cb (session->msg_source,
+ session->current_message->cb,
+ session->current_message->cb_arg);
+
+ /* Arrange *not* to have the message contents monitored. This is
+ purely to avoid overwhelming the application with data. */
+ sio_set_monitorcb (conn, NULL, NULL);
+
+ /* Make sure we read the message from the beginning and get
+ the header processing right. */
+ msg_rewind (session->msg_source);
+ reset_header_table (session->current_message);
+
+ /* Read and process header lines from the application.
+ This step in processing
+ i) removes headers provided by the application that should not be
+ present in the message, e.g. Return-Path: which is added by
+ an MTA during delivery but should not be present in a message
+ being submitted or which is in transit.
+ ii) copies certain headers verbatim, e.g. MIME headers.
+ iii) alters the content of certain headers. This will happen
+ according to library options set up by the application.
+ */
+ errno = 0;
+ while ((line = msg_gets (session->msg_source, &len, 0)) != NULL)
+ {
+ /* Header processing stops at a line containing only CRLF */
+ if (len == 2 && line[0] == '\r' && line[1] == '\n')
+ break;
+
+ /* Check for continuation lines indicated by a blank or tab
+ at the start of the next line. */
+ while ((c = msg_nextc (session->msg_source)) != -1)
+ {
+ if (c != ' ' && c != '\t')
+ break;
+ line = msg_gets (session->msg_source, &len, 1);
+ if (line == NULL)
+ goto break_2;
+ }
+
+ /* Line points to one or more lines of text forming an RFC 2822
+ header. */
+
+ /* Header processing. This function takes the "raw" header from
+ the application and returns a header which is to be written
+ to the remote MTA. If header is NULL this header has been
+ deleted by the library. If header == line it is passed
+ unchanged otherwise, header must be freed after use. */
+ header = process_header (session->current_message, line, &len);
+ if (header != NULL && len > 0)
+ {
+ /* Notify byte count to the application. */
+ if (session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_MESSAGEDATA,
+ session->event_cb_arg,
+ session->current_message, len);
+
+ /* During data transfer, if we are monitoring the message
+ headers, call the monitor callback directly, once per header.
+ We don't bother with monitoring the dot stuffing. Also set
+ the value of the writing parameter to 2 so that the app can
+ distinguish headers from data written in the sio_ package. */
+ if (session->monitor_cb && session->monitor_cb_headers)
+ (*session->monitor_cb) (header, len, SMTP_CB_HEADERS,
+ session->monitor_cb_arg);
+
+ /* Write the header using dot stuffing. N.B. because of
+ dot stuffing, it is necessary to find the line breaks
+ during the copy. */
+ for (pline = header; pline < header + len; pline = p)
+ {
+ p = memchr (pline, '\n', header + len - pline);
+ if (p == NULL)
+ {
+ set_errno (ERANGE);
+ session->cmd_state = session->rsp_state = -1;
+ return;
+ }
+ if (pline[0] == '.')
+ sio_write (conn, ".", 1);
+ sio_write (conn, pline, ++p - pline);
+ }
+ }
+ errno = 0;
+ }
+break_2:
+ if (errno != 0)
+ {
+ /* An error occurred during processing. The only thing that can
+ be done is to drop the connection to the server since SMTP has
+ no way to recover gracefully from client errors while transferring
+ the message. */
+ set_errno (errno);
+ session->cmd_state = session->rsp_state = -1;
+ return;
+ }
+
+ /* Now send missing headers. This completes the processing started
+ above. If the application did not supply certain headers that
+ should be present, the library will supply them here, e.g. Date:,
+ Message-Id: or To:/Cc:/Bcc: headers. In the most extreme case the
+ application might just send a CRLF followed by the message body.
+ Libesmtp will then provide all the necessary headers. */
+ while ((header = missing_header (session->current_message, &len)) != NULL)
+ if (len > 0)
+ {
+ /* Notify byte count to the application. */
+ if (session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_MESSAGEDATA,
+ session->event_cb_arg,
+ session->current_message, len);
+
+ if (session->monitor_cb && session->monitor_cb_headers)
+ (*session->monitor_cb) (header, len, SMTP_CB_HEADERS,
+ session->monitor_cb_arg);
+ for (pline = header; pline < header + len; pline = p)
+ {
+ p = memchr (pline, '\n', header + len - pline);
+ if (p == NULL)
+ {
+ set_errno (ERANGE);
+ session->cmd_state = session->rsp_state = -1;
+ return;
+ }
+ if (pline[0] == '.')
+ sio_write (conn, ".", 1);
+ sio_write (conn, pline, ++p - pline);
+ }
+ }
+
+ /* ... and finally terminate the message headers */
+ sio_write (conn, "\r\n", 2);
+
+ /* Read message body lines from the application and write them
+ to the remote MTA using dot stuffing. */
+ errno = 0;
+ while ((line = msg_gets (session->msg_source, &len, 0)) != NULL)
+ {
+ /* Notify byte count to the application. */
+ if (session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_MESSAGEDATA,
+ session->event_cb_arg,
+ session->current_message, len);
+
+ if (line[0] == '.')
+ sio_write (conn, ".", 1);
+ sio_write (conn, line, len);
+ errno = 0;
+ }
+ if (errno != 0)
+ {
+ set_errno (errno);
+ session->cmd_state = session->rsp_state = -1;
+ return;
+ }
+
+ /* Terminate the DATA command. Explicitly flush the buffer here.
+ This would have happened in the protocol loop anyway but doing it
+ here makes the output of strace more intuitive. */
+ sio_write (conn, ".\r\n", 3);
+ sio_flush (conn);
+
+ sio_set_timeout (conn, session->data2_timeout);
+ session->cmd_state = -1;
+}
+
+void
+rsp_data2 (siobuf_t conn, smtp_session_t session)
+{
+ int code;
+ smtp_recipient_t recipient;
+
+ /* Reinstate the protocol monitor. */
+ if (session->monitor_cb != NULL)
+ sio_set_monitorcb (conn, session->monitor_cb, session->monitor_cb_arg);
+
+ code = read_smtp_response (conn, session,
+ &session->current_message->message_status,
+ NULL);
+ if (code < 0)
+ {
+ session->rsp_state = S_quit;
+ return;
+ }
+
+ if (code == 2)
+ {
+ /* Mark all the recipients complete for which the MTA has accepted
+ responsibility for delivery. */
+ for (recipient = session->current_message->recipients;
+ recipient != NULL;
+ recipient = recipient->next)
+ if (!recipient->complete
+ && recipient->status.code >= 200 && recipient->status.code <= 299)
+ recipient->complete = 1;
+ }
+ else if (code == 5)
+ {
+ /* Mark all the recipients complete. This message cannot be
+ accepted for any recipients. */
+ for (recipient = session->current_message->recipients;
+ recipient != NULL;
+ recipient = recipient->next)
+ recipient->complete = 1;
+ }
+
+
+ if (session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_MESSAGESENT,
+ session->event_cb_arg, session->current_message);
+
+ if (next_message (session))
+ session->rsp_state = (code == 2) ? initial_transaction_state (session)
+ : S_rset;
+ else
+ session->rsp_state = S_quit;
+}
+
+/*****************************************************************************
+ * RSET
+ *****************************************************************************/
+
+void
+cmd_rset (siobuf_t conn, smtp_session_t session)
+{
+ sio_write (conn, "RSET\r\n", 6);
+ if (session->current_message != NULL)
+ session->cmd_state = initial_transaction_state (session);
+ else
+ session->cmd_state = S_quit;
+}
+
+void
+rsp_rset (siobuf_t conn, smtp_session_t session)
+{
+ struct smtp_status status;
+
+ /* The RSET command should always succeed, since this client never
+ attempts to sent trailing whitespace or parameters to the command */
+ memset (&status, 0, sizeof status);
+ read_smtp_response (conn, session, &status, NULL);
+ reset_status (&status);
+ if (session->current_message != NULL)
+ session->rsp_state = initial_transaction_state (session);
+ else
+ session->rsp_state = S_quit;
+}
+
+/*****************************************************************************
+ * QUIT
+ *****************************************************************************/
+
+void
+cmd_quit (siobuf_t conn, smtp_session_t session)
+{
+ sio_write (conn, "QUIT\r\n", 6);
+ session->cmd_state = -1;
+}
+
+void
+rsp_quit (siobuf_t conn, smtp_session_t session)
+{
+ struct smtp_status status;
+
+ /* The QUIT command should always succeed.
+ */
+ memset (&status, 0, sizeof status);
+ read_smtp_response (conn, session, &status, NULL);
+ reset_status (&status);
+ session->rsp_state = -1;
+}
+
+/*****************************************************************************
+ * Other crud.
+ *****************************************************************************/
+
+#ifdef USE_XUSR
+void
+cmd_xusr (siobuf_t conn, smtp_session_t session)
+{
+ sio_write (conn, "XUSR\r\n", 6);
+ session->cmd_state = -1;
+}
+
+void
+rsp_xusr (siobuf_t conn, smtp_session_t session)
+{
+ struct smtp_status status;
+
+ /* The XUSR command should always succeed? */
+ memset (&status, 0, sizeof status);
+ read_smtp_response (conn, session, &status, NULL);
+ reset_status (&status);
+ session->rsp_state = S_mail;
+}
+#endif
diff --git a/protocol.h b/protocol.h
new file mode 100644
index 0000000..f755f3c
--- /dev/null
+++ b/protocol.h
@@ -0,0 +1,43 @@
+#ifndef _protocol_h
+#define _protocol_h
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* Create some symbolic state numbers */
+enum states
+ {
+#define S(x) S_##x,
+#include "protocol-states.h"
+ S__swallow_comma /* required for strict ANSI compiles */
+ };
+
+/* Forward declare protocol command/response handlers */
+#define S(x) void cmd_##x (siobuf_t conn, smtp_session_t session); \
+ void rsp_##x (siobuf_t conn, smtp_session_t session);
+#include "protocol-states.h"
+
+int read_smtp_response (siobuf_t conn, smtp_session_t session,
+ struct smtp_status *status,
+ int (*cb) (smtp_session_t, char *));
+void reset_status (struct smtp_status *status);
+
+#endif
diff --git a/rfc2822date.c b/rfc2822date.c
new file mode 100644
index 0000000..d673297
--- /dev/null
+++ b/rfc2822date.c
@@ -0,0 +1,118 @@
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include <missing.h> /* declarations for missing library functions */
+
+#if TM_IN_SYS_TIME
+# include <sys/time.h>
+# if TIME_WITH_SYS_TIME
+# include <time.h>
+# endif
+#else
+# include <time.h>
+#endif
+
+#include "rfc2822date.h"
+
+#if !HAVE_STRUCT_TM_TM_ZONE
+/* Calculate seconds since 1970 from the struct tm. Leap seconds are
+ ignored, tm must be normalised and the time zone is ignored.
+ If called with a struct tm filled in by localtime() the result
+ will be wrong by the number of seconds the current timezone is
+ offset from GMT! */
+
+#define EPOCH 1970
+
+static time_t
+libesmtp_mktime (struct tm *tm)
+{
+ time_t when;
+ int day, year;
+ static const int days[] =
+ {
+ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
+ };
+
+ year = tm->tm_year + 1900;
+ day = days[tm->tm_mon] + tm->tm_mday - 1;
+
+ /* adjust for leap years paying attention to January and February */
+ day += (year - (EPOCH - (EPOCH % 4))) / 4;
+ day -= (year - (EPOCH - (EPOCH % 100))) / 100;
+ day += (year - (EPOCH - (EPOCH % 400))) / 400;
+ if (tm->tm_mon < 2 && (year % 4 == 0) && (year % 100 != 0 || year / 400 == 0))
+ day -= 1;
+
+ when = ((year - EPOCH) * 365 + day) * 24 + tm->tm_hour;
+ when = (when * 60 + tm->tm_min) * 60 + tm->tm_sec;
+ return when;
+}
+#endif
+
+static const char *days[] =
+ { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", };
+static const char *months[] =
+ { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", };
+
+char *
+rfc2822date (char buf[], size_t buflen, time_t *timedate)
+{
+ struct tm *tm;
+#if HAVE_LOCALTIME_R
+ struct tm tmbuf;
+#endif
+ int dir, minutes;
+#if !HAVE_STRUCT_TM_TM_ZONE
+ time_t gmtoff;
+#endif
+
+#if HAVE_LOCALTIME_R
+ tm = localtime_r (timedate, &tmbuf);
+#else
+ tm = localtime (timedate);
+#endif
+#if HAVE_STRUCT_TM_TM_ZONE
+ minutes = tm->tm_gmtoff / 60;
+#else
+ gmtoff = libesmtp_mktime (tm);
+ gmtoff -= *timedate;
+ minutes = gmtoff / 60;
+#endif
+
+ dir = (minutes > 0) ? '+' : '-';
+ if (minutes < 0)
+ minutes = -minutes;
+ snprintf (buf, buflen, "%s, %d %s %d %02d:%02d:%02d %c%02d%02d",
+ days[tm->tm_wday],
+ tm->tm_mday, months[tm->tm_mon], tm->tm_year + 1900,
+ tm->tm_hour, tm->tm_min, tm->tm_sec,
+ dir, minutes / 60, minutes % 60);
+ return buf;
+}
diff --git a/rfc2822date.h b/rfc2822date.h
new file mode 100644
index 0000000..0fd0cd8
--- /dev/null
+++ b/rfc2822date.h
@@ -0,0 +1,27 @@
+#ifndef _rfc2822date_h
+#define _rfc2822date_h
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+char *rfc2822date (char buf[], size_t buflen, time_t *timedate);
+
+#endif
diff --git a/siobuf.c b/siobuf.c
new file mode 100644
index 0000000..ba384bc
--- /dev/null
+++ b/siobuf.c
@@ -0,0 +1,647 @@
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include <missing.h> /* declarations for missing library functions */
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/poll.h>
+#include <unistd.h>
+
+#ifdef USE_TLS
+# include <openssl/ssl.h>
+#endif
+
+#include "siobuf.h"
+
+#ifdef USE_TLS
+static int sio_sslpoll (struct siobuf *sio, int ret);
+#endif
+
+/* Socket I/O buffering */
+struct siobuf
+ {
+ int sdr; /* Socket descriptor being buffered. */
+ int sdw; /* Socket descriptor being buffered. */
+
+ size_t buffer_size; /* size of buffers */
+ int milliseconds; /* Timeout in ms */
+
+ char *read_buffer; /* client read buffer */
+ char *read_position; /* client read buffer pointer */
+ int read_unread; /* number of bytes unread in buffer */
+
+ char *write_buffer; /* client write buffer */
+ char *write_position; /* client write buffer pointer */
+ char *flush_mark; /* don't flush beyond this point */
+ int write_available; /* number of bytes available in buffer */
+
+ monitorcb_t monitor_cb;
+ void *cbarg;
+
+ recodecb_t encode_cb; /* encoder for outbound data */
+ recodecb_t decode_cb; /* decoder for inbound data */
+ void *secarg;
+
+#ifdef USE_TLS
+ SSL *ssl; /* The SSL connection */
+#endif
+
+ void *user_data;
+ };
+
+/* Attach bi-directional buffering to the socket descriptor.
+ */
+struct siobuf *
+sio_attach (int sdr, int sdw, int buffer_size)
+{
+ struct siobuf *sio;
+
+ sio = malloc (sizeof (struct siobuf));
+ if (sio == NULL)
+ return NULL;
+ memset (sio, 0, sizeof (struct siobuf));
+ sio->sdr = sdr;
+ sio->sdw = sdw;
+
+ /* Use non blocking io and polling to avoid the potential deadlock
+ PIPELINING situation described in RFC 2920. */
+ fcntl (sio->sdw, F_SETFL, O_NONBLOCK);
+ if (sio->sdr != sio->sdw)
+ fcntl (sio->sdr, F_SETFL, O_NONBLOCK);
+
+ /* Allocate the buffer for reading. */
+ sio->buffer_size = buffer_size;
+ sio->read_position = sio->read_buffer = malloc (sio->buffer_size);
+ sio->read_unread = 0;
+ if (sio->read_buffer == NULL)
+ {
+ free (sio);
+ return NULL;
+ }
+
+ /* Allocate the buffer for writing. */
+ sio->write_position = sio->write_buffer = malloc (sio->buffer_size);
+ if (sio->write_buffer == NULL)
+ {
+ free (sio->read_buffer);
+ free (sio);
+ return NULL;
+ }
+ sio->write_available = sio->buffer_size;
+
+ sio->milliseconds = -1;
+ return sio;
+}
+
+/* Detach buffering from the socket descriptor. The socket is not closed.
+ */
+void
+sio_detach (struct siobuf *sio)
+{
+ assert (sio != NULL);
+
+#ifdef USE_TLS
+ if (sio->ssl != NULL)
+ {
+ int ret;
+
+ /* Send a close notify to the peer for a graceful shutdown.
+ */
+ while ((ret = SSL_shutdown (sio->ssl)) == 0)
+ if (sio_sslpoll (sio, ret) <= 0)
+ break;
+ SSL_free (sio->ssl);
+ }
+#endif
+ free (sio->read_buffer);
+ free (sio->write_buffer);
+ free (sio);
+}
+
+void
+sio_set_monitorcb (struct siobuf *sio, monitorcb_t cb, void *arg)
+{
+ assert (sio != NULL);
+
+ sio->monitor_cb = cb;
+ sio->cbarg = arg;
+}
+
+void
+sio_set_timeout (struct siobuf *sio, int milliseconds)
+{
+ assert (sio != NULL);
+
+ sio->milliseconds = milliseconds;
+#ifdef USE_TLS
+ if (sio->ssl != NULL)
+ {
+ long ssl_timeout;
+
+ if (milliseconds < 0)
+ ssl_timeout = 86400L;
+ else
+ ssl_timeout = ((long) milliseconds + 999L) / 1000L;
+ SSL_SESSION_set_timeout (SSL_get_session (sio->ssl), ssl_timeout);
+ }
+#endif
+}
+
+#ifdef USE_TLS
+int
+sio_set_tlsclient_ssl (struct siobuf *sio, SSL *ssl)
+{
+ int ret;
+
+ assert (sio != NULL);
+
+ if (ssl != NULL)
+ {
+ sio->ssl = ssl;
+ SSL_set_rfd (sio->ssl, sio->sdr);
+ SSL_set_wfd (sio->ssl, sio->sdw);
+ while ((ret = SSL_connect (sio->ssl)) <= 0)
+ if (sio_sslpoll (sio, ret) <= 0)
+ {
+ SSL_free (sio->ssl);
+ sio->ssl = NULL;
+ break;
+ }
+ sio_set_timeout (sio, sio->milliseconds);
+ }
+ return sio->ssl != NULL;
+}
+
+int
+sio_set_tlsserver_ssl (struct siobuf *sio, SSL *ssl)
+{
+ int ret;
+
+ assert (sio != NULL);
+
+ if (ssl != NULL)
+ {
+ sio->ssl = ssl;
+ SSL_set_rfd (sio->ssl, sio->sdr);
+ SSL_set_wfd (sio->ssl, sio->sdw);
+ while ((ret = SSL_accept (sio->ssl)) <= 0)
+ if (sio_sslpoll (sio, ret) <= 0)
+ {
+ SSL_free (sio->ssl);
+ sio->ssl = NULL;
+ break;
+ }
+ sio_set_timeout (sio, sio->milliseconds);
+ }
+ return sio->ssl != NULL;
+}
+#endif
+
+void
+sio_set_securitycb (struct siobuf *sio,
+ recodecb_t encode_cb, recodecb_t decode_cb, void *arg)
+{
+ assert (sio != NULL);
+
+ sio->secarg = arg;
+ sio->encode_cb = encode_cb;
+ sio->decode_cb = decode_cb;
+}
+
+/* Return -1 on timeout or error. Return 0 if nothing to poll.
+ Return OR of SIO_READ, SIO_WRITE as appropriate for request.
+ If the fast flag is set, poll does not block, otherwise it
+ blocks with the current timeout value. */
+int
+sio_poll (struct siobuf *sio, int want_read, int want_write, int fast)
+{
+ int npoll, status, rval;
+ struct pollfd pollfd[2];
+
+ assert (sio != NULL);
+
+ if (want_read && sio->read_unread > 0)
+ return SIO_READ;
+#ifdef USE_TLS
+ /* SSL_read() returns data a record at a time, however it is possible
+ that more than one record was read from the socket. If this happens
+ poll() will not report data waiting to be read but SSL_read() will
+ return the next record. Using SSL_pending() solves this problem.
+ */
+ if (want_read && sio->ssl != NULL && SSL_pending (sio->ssl))
+ return SIO_READ;
+#endif
+
+ npoll = 0;
+ if (want_read)
+ {
+ pollfd[npoll].fd = sio->sdr;
+ pollfd[npoll].events = POLLIN;
+ pollfd[npoll].revents = 0;
+ npoll += 1;
+ }
+ if (want_write)
+ {
+ pollfd[npoll].fd = sio->sdw;
+ pollfd[npoll].events = POLLOUT;
+ pollfd[npoll].revents = 0;
+ npoll += 1;
+ }
+ if (npoll == 0)
+ return 0;
+
+ while ((status = poll (pollfd, npoll, fast ? 0 : sio->milliseconds)) < 0)
+ if (errno != EINTR)
+ return -1;
+
+ /* Timeout is not an error on the fast poll */
+ if (status == 0 && fast)
+ return 0;
+
+ rval = 0;
+ while (--npoll >= 0)
+ {
+ if (pollfd[npoll].revents & POLLIN)
+ rval |= SIO_READ;
+ if (pollfd[npoll].revents & POLLOUT)
+ rval |= SIO_WRITE;
+ }
+ return (rval > 0) ? rval : -1;
+}
+
+#ifdef USE_TLS
+static int
+sio_sslpoll (struct siobuf *sio, int ret)
+{
+ int err, want_read, want_write;
+
+ assert (sio != NULL);
+
+ err = SSL_get_error (sio->ssl, ret);
+ want_read = want_write = 0;
+ if (err == SSL_ERROR_WANT_READ)
+ want_read = 1;
+ else if (err == SSL_ERROR_WANT_WRITE)
+ want_write = 1;
+ else
+ return -1;
+ return sio_poll (sio, want_read, want_write, 0);
+}
+#endif
+
+void
+sio_write (struct siobuf *sio, const void *bufp, int buflen)
+{
+ const char *buf = bufp;
+
+ assert (sio != NULL && buf != NULL);
+
+ if (buflen < 0)
+ buflen = strlen (buf);
+ if (buflen == 0)
+ return;
+
+ while (buflen > sio->write_available)
+ {
+ if (sio->write_available > 0)
+ {
+ memcpy (sio->write_position, buf, sio->write_available);
+ sio->write_position += sio->write_available;
+ buf += sio->write_available;
+ buflen -= sio->write_available;
+ }
+ sio_flush (sio);
+ assert (sio->write_available > 0);
+ }
+ if (buflen > 0)
+ {
+ memcpy (sio->write_position, buf, buflen);
+ sio->write_position += buflen;
+ sio->write_available -= buflen;
+ /* If the buffer is exactly filled, flush it */
+ if (sio->write_available == 0)
+ sio_flush (sio);
+ }
+}
+
+static void
+raw_write (struct siobuf *sio, const char *buf, int len)
+{
+ int n, total, status;
+ struct pollfd pollfd;
+
+ assert (sio != NULL && buf != NULL);
+
+ for (total = 0; total < len; total += n)
+#ifdef USE_TLS
+ if (sio->ssl != NULL)
+ {
+ /* SSL_write() writes a record a time. The outer loop calls
+ it repeatedly until all the write buffer contents have
+ been written. The inner loop handles EAGAIN (EWOULDBLOCK)
+ propagating up through OpenSSL. */
+ while ((n = SSL_write (sio->ssl, buf, len)) <= 0)
+ if (sio_sslpoll (sio, n) <= 0)
+ return;
+ }
+ else
+#endif
+ {
+ /* Its conceiveable that write() actually writes less than
+ requested. The outer loop calls this until all of the write
+ buffer has been written. The inner loop handles blocking
+ in poll() and errors */
+ pollfd.fd = sio->sdw;
+ pollfd.events = POLLOUT;
+ errno = 0;
+ while ((n = write (sio->sdw, buf + total, len - total)) < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ if (errno != EAGAIN)
+ return;
+
+ pollfd.revents = 0;
+ while ((status = poll (&pollfd, 1, sio->milliseconds)) < 0)
+ if (errno != EINTR)
+ return;
+ if (status == 0)
+ {
+ errno = ETIMEDOUT;
+ return;
+ }
+ if (!(pollfd.revents & POLLOUT))
+ return;
+ errno = 0;
+ }
+ }
+}
+
+void
+sio_flush (struct siobuf *sio)
+{
+ int length;
+
+ assert (sio != NULL);
+
+ if (sio->flush_mark != NULL && sio->flush_mark > sio->write_buffer)
+ length = sio->flush_mark - sio->write_buffer;
+ else
+ length = sio->write_position - sio->write_buffer;
+ if (length <= 0)
+ return;
+
+ if (sio->monitor_cb != NULL)
+ (*sio->monitor_cb) (sio->write_buffer, length, 1, sio->cbarg);
+
+ if (sio->encode_cb != NULL)
+ {
+ char *buf;
+ int len;
+
+ /* Rules for the encode callback.
+
+ The output variables (here buf and len) may be set to the
+ write_buffer iff the encoding can be performed in place and
+ the result is shorter than the original data. Otherwise the
+ callback must maintain its own buffer which must persist until
+ the next call in the same thread. The secarg argument may be
+ used to maintain this buffer. */
+ (*sio->encode_cb) (&buf, &len, sio->write_buffer, length, sio->secarg);
+ raw_write (sio, buf, len);
+ }
+ else
+ raw_write (sio, sio->write_buffer, length);
+
+ if (sio->flush_mark != NULL && sio->flush_mark > sio->write_buffer)
+ {
+ length = sio->write_position - sio->flush_mark;
+ if (length > 0)
+ memmove (sio->write_buffer, sio->flush_mark, length);
+ }
+ else
+ length = 0;
+ sio->write_available = sio->buffer_size - length;
+ sio->write_position = sio->write_buffer + length;
+ sio->flush_mark = NULL;
+}
+
+void
+sio_mark (struct siobuf *sio)
+{
+ assert (sio != NULL);
+
+ sio->flush_mark = sio->write_position;
+}
+
+/* N.B. raw_read() requires a non-blocking read, otherwise it would
+ block indefinitely instead of timing out. Normally the poll()
+ should not be needed since the protocol level will have polled
+ before reading or writing. */
+static int
+raw_read (struct siobuf *sio, char *buf, int len)
+{
+ int n, status;
+ struct pollfd pollfd;
+
+ assert (sio != NULL && buf != NULL && len > 0);
+
+#ifdef USE_TLS
+ if (sio->ssl != NULL)
+ {
+ /* SSL_read() reads complete records from the network and returns
+ one record at a time. This means that poll() may indicate that
+ there is no data waiting to be read even though SSL_read() will
+ return the next record. SSL_pending() is used to avoid this
+ problem. The loop handles EAGAIN (EWOULDBLOCK) propagating up
+ through OpenSSL. */
+ while ((n = SSL_read (sio->ssl, buf, len)) < 0)
+ if (sio_sslpoll (sio, n) <= 0)
+ break;
+ }
+ else
+#endif
+ {
+ pollfd.fd = sio->sdr;
+ pollfd.events = POLLIN;
+ errno = 0;
+ while ((n = read (sio->sdr, buf, len)) < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ if (errno != EAGAIN)
+ return 0;
+
+ pollfd.revents = 0;
+ while ((status = poll (&pollfd, 1, sio->milliseconds)) < 0)
+ if (errno != EINTR)
+ return 0;
+ if (status == 0)
+ {
+ errno = ETIMEDOUT;
+ return 0;
+ }
+ if (!(pollfd.revents & POLLIN))
+ return 0;
+ errno = 0;
+ }
+ }
+ return n;
+}
+
+int
+sio_fill (struct siobuf *sio)
+{
+ assert (sio != NULL);
+
+ sio->read_unread = raw_read (sio, sio->read_buffer, sio->buffer_size);
+ if (sio->read_unread <= 0)
+ return 0;
+
+ if (sio->decode_cb != NULL)
+ /* Rules for the decode callback.
+
+ The output variables (here buf and len) may be set to the
+ read_buffer iff the decoding can be performed in place and
+ the result is shorter than the original data. Otherwise the
+ callback must maintain its own buffer which must persist until
+ the next call in the same thread. The secarg argument may be
+ used to maintain this buffer. */
+ (*sio->decode_cb) (&sio->read_position, &sio->read_unread,
+ sio->read_buffer, sio->read_unread, sio->secarg);
+ else
+ sio->read_position = sio->read_buffer;
+
+ if (sio->monitor_cb != NULL && sio->read_unread > 0)
+ (*sio->monitor_cb) (sio->read_position, sio->read_unread,
+ 0, sio->cbarg);
+ return sio->read_unread > 0;
+}
+
+int
+sio_read (struct siobuf *sio, void *bufp, int buflen)
+{
+ char *buf = bufp;
+ int count, total;
+
+ assert (sio != NULL && buf != NULL && buflen > 0);
+
+ if (sio->read_unread <= 0 && !sio_fill (sio))
+ return -1;
+
+ total = 0;
+ do
+ while (sio->read_unread > 0)
+ {
+ if ((count = sio->read_unread) > buflen)
+ count = buflen;
+ memcpy (buf, sio->read_position, count);
+ sio->read_position += count;
+ sio->read_unread -= count;
+
+ total += count;
+ if ((buflen -= count) <= 0)
+ return total;
+ buf += count;
+ }
+ while (sio_fill (sio));
+ return total;
+}
+
+char *
+sio_gets (struct siobuf *sio, char buf[], int buflen)
+{
+ int c;
+ char *p;
+
+ assert (sio != NULL && buf != NULL && buflen > 0);
+
+ if (sio->read_unread <= 0 && !sio_fill (sio))
+ return NULL;
+
+ p = buf;
+ do
+ while (sio->read_unread > 0)
+ {
+ c = *sio->read_position++;
+ sio->read_unread--;
+ *p++ = c;
+ buflen--;
+ if (c == '\n' || buflen <= 1)
+ {
+ *p = '\0';
+ return buf;
+ }
+ }
+ while (sio_fill (sio));
+ *p = '\0';
+ return buf;
+}
+
+void *
+sio_set_userdata (struct siobuf *sio, void *user_data)
+{
+ void *old = sio->user_data;
+
+ sio->user_data = user_data;
+ return old;
+}
+
+void *
+sio_get_userdata (struct siobuf *sio)
+{
+ return sio->user_data;
+}
+
+int
+sio_printf (struct siobuf *sio, const char *format, ...)
+{
+ va_list alist;
+ char buf[1024];
+ int len;
+
+ assert (sio != NULL && format != NULL);
+
+ va_start (alist, format);
+ len = vsnprintf (buf, sizeof buf, format, alist);
+ va_end (alist);
+ if (len >= (int) sizeof buf - 1)
+ len = sizeof buf - 1;
+ if (len > 0)
+ sio_write (sio, buf, len);
+ return len;
+}
diff --git a/siobuf.h b/siobuf.h
new file mode 100644
index 0000000..577f717
--- /dev/null
+++ b/siobuf.h
@@ -0,0 +1,59 @@
+#ifndef _siobuf_h
+#define _siobuf_h
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+typedef struct siobuf *siobuf_t;
+
+#define SIO_BUFSIZE 2048 /* arbitrary, not too short, not too long */
+#define SIO_READ 1
+#define SIO_WRITE 2
+
+typedef void (*recodecb_t) (char **dstbuf, int *dstlen,
+ const char *srcbuf, int srclen, void *arg);
+typedef void (*monitorcb_t) (const char *buffer, int length, int direction,
+ void *arg);
+
+struct siobuf *sio_attach(int sdr, int sdw, int buffer_size);
+void sio_detach(struct siobuf *sio);
+void sio_set_monitorcb(struct siobuf *sio, monitorcb_t cb, void *arg);
+void sio_set_timeout(struct siobuf *sio, int milliseconds);
+void sio_set_securitycb(struct siobuf *sio, recodecb_t encode_cb,
+ recodecb_t decode_cb, void *arg);
+int sio_poll(struct siobuf *sio,int want_read, int want_write, int fast);
+void sio_write(struct siobuf *sio, const void *bufp, int buflen);
+void sio_flush(struct siobuf *sio);
+void sio_mark(struct siobuf *sio);
+int sio_fill(struct siobuf *sio);
+int sio_read(struct siobuf *sio, void *bufp, int buflen);
+char *sio_gets(struct siobuf *sio, char buf[], int buflen);
+int sio_printf(struct siobuf *sio, const char *format, ...)
+ __attribute__ ((format (printf, 2, 3))) ;
+void *sio_set_userdata (struct siobuf *sio, void *user_data);
+void *sio_get_userdata (struct siobuf *io);
+
+
+#ifdef USE_TLS
+int sio_set_tlsclient_ssl (struct siobuf *sio, SSL *ssl);
+int sio_set_tlsserver_ssl (struct siobuf *sio, SSL *ssl);
+#endif
+#endif
diff --git a/smtp-api.c b/smtp-api.c
new file mode 100644
index 0000000..25c8849
--- /dev/null
+++ b/smtp-api.c
@@ -0,0 +1,637 @@
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include <missing.h> /* declarations for missing library functions */
+
+#include <errno.h>
+#include "api.h"
+#include "libesmtp-private.h"
+#include "headers.h"
+
+/* This file contains the SMTP client library's external API. For the
+ most part, it just sanity checks function arguments and either carries
+ out the simple stuff directly, or passes complicated stuff into the
+ bowels of the library and RFC hell.
+ */
+
+smtp_session_t
+smtp_create_session (void)
+{
+ smtp_session_t session;
+
+ if ((session = malloc (sizeof (struct smtp_session))) == NULL)
+ {
+ set_errno (ENOMEM);
+ return 0;
+ }
+
+ memset (session, 0, sizeof (struct smtp_session));
+
+ /* Set the default timeouts to the minimum values described in RFC 2822 */
+ session->greeting_timeout = GREETING_DEFAULT;
+ session->envelope_timeout = ENVELOPE_DEFAULT;
+ session->data_timeout = DATA_DEFAULT;
+ session->transfer_timeout = TRANSFER_DEFAULT;
+ session->data2_timeout = DATA2_DEFAULT;
+
+ return session;
+}
+
+int
+smtp_set_server (smtp_session_t session, const char *hostport)
+{
+ char *host, *service;
+
+ SMTPAPI_CHECK_ARGS (session != NULL && hostport != NULL, 0);
+
+ if (session->host != NULL)
+ {
+ free (session->host);
+ session->port = session->host = NULL;
+ }
+
+ if ((host = strdup (hostport)) == NULL)
+ {
+ set_errno (ENOMEM);
+ return 0;
+ }
+
+ if ((service = strchr (host, ':')) != NULL)
+ *service++ = '\0';
+
+ if (service == NULL)
+ session->port = "587";
+ else
+ session->port = service;
+ session->host = host;
+ return 1;
+}
+
+int
+smtp_set_hostname (smtp_session_t session, const char *hostname)
+{
+#ifdef HAVE_GETHOSTNAME
+ SMTPAPI_CHECK_ARGS (session != NULL, 0);
+#else
+ SMTPAPI_CHECK_ARGS (session != NULL && hostname != NULL, 0);
+#endif
+
+ if (session->localhost != NULL)
+ free (session->localhost);
+#ifdef HAVE_GETHOSTNAME
+ if (hostname == NULL)
+ {
+ session->localhost = NULL;
+ return 1;
+ }
+#endif
+ session->localhost = strdup (hostname);
+ if (session->localhost == NULL)
+ {
+ set_errno (ENOMEM);
+ return 0;
+ }
+ return 1;
+}
+
+smtp_message_t
+smtp_add_message (smtp_session_t session)
+{
+ smtp_message_t message;
+
+ SMTPAPI_CHECK_ARGS (session != NULL, NULL);
+
+ if ((message = malloc (sizeof (struct smtp_message))) == NULL)
+ {
+ set_errno (ENOMEM);
+ return 0;
+ }
+
+ memset (message, 0, sizeof (struct smtp_message));
+ message->session = session;
+ APPEND_LIST (session->messages, session->end_messages, message);
+ return message;
+}
+
+int
+smtp_enumerate_messages (smtp_session_t session,
+ smtp_enumerate_messagecb_t cb, void *arg)
+{
+ smtp_message_t message;
+
+ SMTPAPI_CHECK_ARGS (session != NULL && cb != NULL, 0);
+
+ for (message = session->messages; message != NULL; message = message->next)
+ (*cb) (message, arg);
+ return 1;
+}
+
+const smtp_status_t *
+smtp_message_transfer_status (smtp_message_t message)
+{
+ SMTPAPI_CHECK_ARGS (message != NULL, NULL);
+
+ return &message->message_status;
+}
+
+int
+smtp_set_reverse_path (smtp_message_t message, const char *mailbox)
+{
+ SMTPAPI_CHECK_ARGS (message != NULL, 0);
+
+ if (message->reverse_path_mailbox != NULL)
+ free (message->reverse_path_mailbox);
+ if (mailbox == NULL)
+ message->reverse_path_mailbox = NULL;
+ else if ((message->reverse_path_mailbox = strdup (mailbox)) == NULL)
+ {
+ set_errno (ENOMEM);
+ return 0;
+ }
+ return 1;
+}
+
+const smtp_status_t *
+smtp_reverse_path_status (smtp_message_t message)
+{
+ SMTPAPI_CHECK_ARGS (message != NULL, NULL);
+
+ return &message->reverse_path_status;
+}
+
+int
+smtp_message_reset_status (smtp_message_t message)
+{
+ SMTPAPI_CHECK_ARGS (message != NULL, 0);
+
+ reset_status (&message->reverse_path_status);
+ reset_status (&message->message_status);
+ return 1;
+}
+
+smtp_recipient_t
+smtp_add_recipient (smtp_message_t message, const char *mailbox)
+{
+ smtp_recipient_t recipient;
+
+ SMTPAPI_CHECK_ARGS (message != NULL && mailbox != NULL, NULL);
+
+ if ((recipient = malloc (sizeof (struct smtp_recipient))) == NULL)
+ {
+ set_errno (ENOMEM);
+ return 0;
+ }
+
+ memset (recipient, 0, sizeof (struct smtp_recipient));
+ recipient->message = message;
+ recipient->mailbox = strdup (mailbox);
+ if (recipient->mailbox == NULL)
+ {
+ free (recipient);
+ set_errno (ENOMEM);
+ return 0;
+ }
+
+ APPEND_LIST (message->recipients, message->end_recipients, recipient);
+ return recipient;
+}
+
+int
+smtp_enumerate_recipients (smtp_message_t message,
+ smtp_enumerate_recipientcb_t cb, void *arg)
+{
+ smtp_recipient_t recipient;
+
+ SMTPAPI_CHECK_ARGS (message != NULL, 0);
+
+ for (recipient = message->recipients;
+ recipient != NULL;
+ recipient = recipient->next)
+ (*cb) (recipient, recipient->mailbox, arg);
+ return 1;
+}
+
+const smtp_status_t *
+smtp_recipient_status (smtp_recipient_t recipient)
+{
+ SMTPAPI_CHECK_ARGS (recipient != NULL, NULL);
+
+ return &recipient->status;
+}
+
+int
+smtp_recipient_check_complete (smtp_recipient_t recipient)
+{
+ SMTPAPI_CHECK_ARGS (recipient != NULL, 0);
+
+ return recipient->complete;
+}
+
+int
+smtp_recipient_reset_status (smtp_recipient_t recipient)
+{
+ SMTPAPI_CHECK_ARGS (recipient != NULL, 0);
+
+ reset_status (&recipient->status);
+ recipient->complete = 0;
+ return 1;
+}
+
+/* DSN (RFC 1891) */
+int
+smtp_dsn_set_ret (smtp_message_t message, enum ret_flags flags)
+{
+ SMTPAPI_CHECK_ARGS (message != NULL, 0);
+
+ message->dsn_ret = flags;
+ if (flags != Ret_NOTSET)
+ message->session->required_extensions |= EXT_DSN;
+ return 1;
+}
+
+int
+smtp_dsn_set_envid (smtp_message_t message, const char *envid)
+{
+ SMTPAPI_CHECK_ARGS (message != NULL, 0);
+
+ message->dsn_envid = strdup (envid);
+ if (message->dsn_envid == NULL)
+ {
+ set_errno (ENOMEM);
+ return 0;
+ }
+ message->session->required_extensions |= EXT_DSN;
+ return 1;
+}
+
+int
+smtp_dsn_set_notify (smtp_recipient_t recipient, enum notify_flags flags)
+{
+ SMTPAPI_CHECK_ARGS (recipient != NULL, 0);
+
+ recipient->dsn_notify = flags;
+ if (flags != Notify_NOTSET)
+ recipient->message->session->required_extensions |= EXT_DSN;
+ return 1;
+}
+
+int
+smtp_dsn_set_orcpt (smtp_recipient_t recipient,
+ const char *address_type, const char *address)
+{
+ SMTPAPI_CHECK_ARGS (recipient != NULL, 0);
+
+ recipient->dsn_addrtype = strdup (address_type);
+ if (recipient->dsn_addrtype == NULL)
+ {
+ set_errno (ENOMEM);
+ return 0;
+ }
+ recipient->dsn_orcpt = strdup (address);
+ if (recipient->dsn_orcpt == NULL)
+ {
+ free (recipient->dsn_addrtype);
+ set_errno (ENOMEM);
+ return 0;
+ }
+ recipient->message->session->required_extensions |= EXT_DSN;
+ return 1;
+}
+
+/* SIZE (RFC 1870) */
+int
+smtp_size_set_estimate (smtp_message_t message, unsigned long size)
+{
+ SMTPAPI_CHECK_ARGS (message != NULL, 0);
+
+ message->size_estimate = size;
+ return 1;
+}
+
+/* 8BITMIME (RFC 1652) */
+int
+smtp_8bitmime_set_body (smtp_message_t message, enum e8bitmime_body body)
+{
+ SMTPAPI_CHECK_ARGS (message != NULL, 0);
+#ifndef USE_CHUNKING
+ SMTPAPI_CHECK_ARGS (body != E8bitmime_BINARYMIME, 0);
+#endif
+
+ message->e8bitmime = body;
+#ifdef USE_CHUNKING
+ if (body == E8bitmime_BINARYMIME)
+ message->session->required_extensions |= (EXT_BINARYMIME | EXT_CHUNKING);
+ else
+#endif
+ if (body != E8bitmime_NOTSET)
+ message->session->required_extensions |= EXT_8BITMIME;
+ return 1;
+}
+
+/* DELIVERBY (RFC 2852) */
+int
+smtp_deliverby_set_mode (smtp_message_t message,
+ long time, enum by_mode mode, int trace)
+{
+ SMTPAPI_CHECK_ARGS (message != NULL, 0);
+ SMTPAPI_CHECK_ARGS ((-999999999 <= time && time <= +999999999), 0);
+ SMTPAPI_CHECK_ARGS (!(mode == By_RETURN && time <= 0), 0);
+
+ message->by_time = time;
+ message->by_mode = mode;
+ message->by_trace = !!trace;
+ return 1;
+}
+
+int
+smtp_set_messagecb (smtp_message_t message, smtp_messagecb_t cb, void *arg)
+{
+ SMTPAPI_CHECK_ARGS (message != NULL && cb != NULL, 0);
+
+ message->cb = cb;
+ message->cb_arg = arg;
+ return 1;
+}
+
+int
+smtp_set_eventcb (smtp_session_t session, smtp_eventcb_t cb, void *arg)
+{
+ SMTPAPI_CHECK_ARGS (session != NULL, 0);
+
+ session->event_cb = cb;
+ session->event_cb_arg = arg;
+ return 1;
+}
+
+int
+smtp_set_monitorcb (smtp_session_t session, smtp_monitorcb_t cb, void *arg,
+ int headers)
+{
+ SMTPAPI_CHECK_ARGS (session != NULL, 0);
+
+ session->monitor_cb = cb;
+ session->monitor_cb_arg = arg;
+ session->monitor_cb_headers = headers;
+ return 1;
+}
+
+int
+smtp_start_session (smtp_session_t session)
+{
+ smtp_message_t message;
+
+ SMTPAPI_CHECK_ARGS (session != NULL && session->host != NULL, 0);
+#ifndef HAVE_GETHOSTNAME
+ SMTPAPI_CHECK_ARGS (session->localhost != NULL, 0);
+#endif
+
+ /* Check that every message has a callback set */
+ for (message = session->messages; message != NULL; message = message->next)
+ if (message->cb == NULL)
+ {
+ set_error (SMTP_ERR_INVAL);
+ return 0;
+ }
+
+ return do_session (session);
+}
+
+int
+smtp_destroy_session (smtp_session_t session)
+{
+ smtp_message_t message;
+ smtp_recipient_t recipient;
+
+ SMTPAPI_CHECK_ARGS (session != NULL, 0);
+
+ reset_status (&session->mta_status);
+ destroy_auth_mechanisms (session);
+#ifdef USE_ETRN
+ destroy_etrn_nodes (session);
+#endif
+
+ if (session->host != NULL)
+ free (session->host);
+ if (session->localhost != NULL)
+ free (session->localhost);
+
+ if (session->msg_source != NULL)
+ msg_source_destroy (session->msg_source);
+
+ while (session->messages != NULL)
+ {
+ message = session->messages->next;
+
+ reset_status (&session->messages->message_status);
+ reset_status (&session->messages->reverse_path_status);
+ free (session->messages->reverse_path_mailbox);
+
+ while (session->messages->recipients != NULL)
+ {
+ recipient = session->messages->recipients->next;
+
+ reset_status (&session->messages->recipients->status);
+ free (session->messages->recipients->mailbox);
+
+ if (session->messages->recipients->dsn_addrtype != NULL)
+ free (session->messages->recipients->dsn_addrtype);
+ if (session->messages->recipients->dsn_orcpt != NULL)
+ free (session->messages->recipients->dsn_orcpt);
+
+ free (session->messages->recipients);
+ session->messages->recipients = recipient;
+ }
+
+ destroy_header_table (session->messages);
+
+ if (session->messages->dsn_envid != NULL)
+ free (session->messages->dsn_envid);
+
+ free (session->messages);
+ session->messages = message;
+ }
+
+ free (session);
+ return 1;
+}
+
+void *
+smtp_set_application_data (smtp_session_t session, void *data)
+{
+ void *old;
+
+ SMTPAPI_CHECK_ARGS (session != NULL, 0);
+
+ old = session->application_data;
+ session->application_data = data;
+ return old;
+}
+
+void *
+smtp_get_application_data (smtp_session_t session)
+{
+ SMTPAPI_CHECK_ARGS (session != NULL, 0);
+
+ return session->application_data;
+}
+
+void *
+smtp_message_set_application_data (smtp_message_t message, void *data)
+{
+ void *old;
+
+ SMTPAPI_CHECK_ARGS (message != NULL, 0);
+
+ old = message->application_data;
+ message->application_data = data;
+ return old;
+}
+
+void *
+smtp_message_get_application_data (smtp_message_t message)
+{
+ SMTPAPI_CHECK_ARGS (message != NULL, 0);
+
+ return message->application_data;
+}
+
+void *
+smtp_recipient_set_application_data (smtp_recipient_t recipient, void *data)
+{
+ void *old;
+
+ SMTPAPI_CHECK_ARGS (recipient != NULL, 0);
+
+ old = recipient->application_data;
+ recipient->application_data = data;
+ return old;
+}
+
+void *
+smtp_recipient_get_application_data (smtp_recipient_t recipient)
+{
+ SMTPAPI_CHECK_ARGS (recipient != NULL, 0);
+
+ return recipient->application_data;
+}
+
+int
+smtp_version (void *buf, size_t len, int what)
+{
+ static const char version[] = VERSION;
+
+ SMTPAPI_CHECK_ARGS (buf != NULL && len > 0, 0);
+ SMTPAPI_CHECK_ARGS (what == 0, 0);
+
+ if (len < sizeof version)
+ {
+ set_error (SMTP_ERR_INVAL);
+ return 0;
+ }
+ memcpy (buf, version, sizeof version);
+ return 1;
+}
+
+/* Some applications can't handle one recipient from many failing
+ particularly well. If the 'require_all_recipients' option is
+ set, this will fail the entire transaction even if some of the
+ recipients were accepted in the RCPT commands. */
+int
+smtp_option_require_all_recipients (smtp_session_t session, int state)
+{
+ SMTPAPI_CHECK_ARGS (session != NULL, 0);
+
+ session->require_all_recipients = !!state;
+ return 1;
+}
+
+/* Set the timeouts. An absolute minumum timeout of one second is imposed.
+ Unless overriden using the OVERRIDE_RFC2822_MINIMUM flag, the minimum
+ values recommended in RFC 2822 are enforced. Return value is the actual
+ timeout set or zero on error. */
+long
+smtp_set_timeout (smtp_session_t session, int which, long value)
+{
+ long minimum = 1000L;
+ int override = 0;
+
+ /* ``which'' is checked in the switch below */
+ SMTPAPI_CHECK_ARGS (session != NULL && value > 0, 0);
+
+ override = which & Timeout_OVERRIDE_RFC2822_MINIMUM;
+ if (override)
+ which &= ~Timeout_OVERRIDE_RFC2822_MINIMUM;
+ else
+ switch (which)
+ {
+ case Timeout_GREETING:
+ minimum = GREETING_DEFAULT;
+ break;
+ case Timeout_ENVELOPE:
+ minimum = ENVELOPE_DEFAULT;
+ break;
+ case Timeout_DATA:
+ minimum = DATA_DEFAULT;
+ break;
+ case Timeout_TRANSFER:
+ minimum = TRANSFER_DEFAULT;
+ break;
+ case Timeout_DATA2:
+ minimum = DATA2_DEFAULT;
+ break;
+ }
+
+ if (value < minimum)
+ value = minimum;
+ switch (which)
+ {
+ case Timeout_GREETING:
+ session->greeting_timeout = value;
+ break;
+ case Timeout_ENVELOPE:
+ session->envelope_timeout = value;
+ break;
+ case Timeout_DATA:
+ session->data_timeout = value;
+ break;
+ case Timeout_TRANSFER:
+ session->transfer_timeout = value;
+ break;
+ case Timeout_DATA2:
+ session->data2_timeout = value;
+ break;
+ default:
+ set_error (SMTP_ERR_INVAL);
+ return 0L;
+ }
+
+ return value;
+}
diff --git a/smtp-auth.c b/smtp-auth.c
new file mode 100644
index 0000000..9398367
--- /dev/null
+++ b/smtp-auth.c
@@ -0,0 +1,306 @@
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+
+#ifdef USE_SASL
+/* Support for the SMTP AUTH verb.
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <missing.h> /* declarations for missing library functions */
+
+#include "auth-client.h"
+#include "libesmtp-private.h"
+#include "api.h"
+
+#include "message-source.h"
+#include "siobuf.h"
+#include "tokens.h"
+#include "base64.h"
+#include "protocol.h"
+
+int
+smtp_auth_set_context (smtp_session_t session, auth_context_t context)
+{
+ SMTPAPI_CHECK_ARGS (session != NULL, 0);
+
+ session->auth_context = context;
+ return 1;
+}
+
+struct mechanism
+ {
+ struct mechanism *next;
+ char *name;
+ };
+
+void
+destroy_auth_mechanisms (smtp_session_t session)
+{
+ struct mechanism *mech, *next;
+
+ for (mech = session->auth_mechanisms; mech != NULL; mech = next)
+ {
+ next = mech->next;
+ if (mech->name != NULL) /* or assert (mech->name != NULL); */
+ free (mech->name);
+ free (mech);
+ }
+ session->current_mechanism = session->auth_mechanisms = NULL;
+}
+
+void
+set_auth_mechanisms (smtp_session_t session, const char *mechanisms)
+{
+ char buf[64];
+ struct mechanism *mech;
+
+ while (read_atom (skipblank (mechanisms), &mechanisms, buf, sizeof buf))
+ {
+ /* scan existing list to avoid duplicates */
+ for (mech = session->auth_mechanisms; mech != NULL; mech = mech->next)
+ if (strcasecmp (buf, mech->name) == 0)
+ break;
+ if (mech != NULL)
+ continue;
+
+ /* new mechanism, so add to the list */
+ mech = malloc (sizeof (struct mechanism));
+ if (mech == NULL)
+ {
+ /* FIXME: propagate ENOMEM to app. */
+ continue;
+ }
+ mech->name = strdup (buf);
+ if (mech->name == NULL)
+ {
+ /* FIXME: propagate ENOMEM to app. */
+ free (mech);
+ continue;
+ }
+ APPEND_LIST (session->auth_mechanisms, session->current_mechanism, mech);
+ }
+}
+
+int
+select_auth_mechanism (smtp_session_t session)
+{
+ if (session->authenticated)
+ return 0;
+ if (session->auth_context == NULL)
+ return 0;
+ if (!auth_client_enabled (session->auth_context))
+ return 0;
+ /* find the first usable auth mechanism */
+ for (session->current_mechanism = session->auth_mechanisms;
+ session->current_mechanism != NULL;
+ session->current_mechanism = session->current_mechanism->next)
+ if (auth_set_mechanism (session->auth_context,
+ session->current_mechanism->name))
+ return 1;
+ return 0;
+}
+
+static int
+next_auth_mechanism (smtp_session_t session)
+{
+ /* find the next usable auth mechanism */
+ while ((session->current_mechanism = session->current_mechanism->next) != NULL)
+ if (auth_set_mechanism (session->auth_context,
+ session->current_mechanism->name))
+ return 1;
+ return 0;
+}
+
+void
+cmd_auth (siobuf_t conn, smtp_session_t session)
+{
+ char buf[2048];
+ const char *response;
+ int len;
+
+ assert (session != NULL && session->auth_context != NULL);
+
+ sio_printf (conn, "AUTH %s", auth_mechanism_name (session->auth_context));
+
+ /* Ask SASL for the initial response (if there is one). */
+ response = auth_response (session->auth_context, NULL, &len);
+ if (response != NULL)
+ {
+ /* Encode the response and send it back to the server */
+ len = b64_encode (buf, sizeof buf, response, len);
+ if (len == 0)
+ sio_write (conn, " =", 2);
+ else if (len > 0)
+ {
+ sio_write (conn, " ", 1);
+ sio_write (conn, buf, len);
+ }
+ }
+
+ sio_write (conn, "\r\n", 2);
+ session->cmd_state = -1;
+}
+
+void
+rsp_auth (siobuf_t conn, smtp_session_t session)
+{
+ int code;
+
+ code = read_smtp_response (conn, session, &session->mta_status, NULL);
+ if (code < 0)
+ {
+ session->rsp_state = S_quit;
+ return;
+ }
+ if (code == 4 || code == 5)
+ {
+ /* If auth mechanism is too weak or encryption is required, give up.
+ Otherwise try the next mechanism. */
+ if (session->mta_status.code == 534 || session->mta_status.code == 538)
+ session->rsp_state = S_quit;
+ else
+ {
+ /* If another mechanism cannot be selected, move on to the
+ mail command since the MTA is required to accept mail for
+ its own domain. */
+ if (next_auth_mechanism (session))
+ session->rsp_state = S_auth;
+#ifdef USE_ETRN
+ else if (check_etrn (session))
+ session->rsp_state = S_etrn;
+#endif
+ else
+ session->rsp_state = initial_transaction_state (session);
+ }
+ }
+ else if (code == 2)
+ {
+ session->authenticated = 1;
+ if (auth_get_ssf (session->auth_context) != 0)
+ {
+ /* Add SASL mechanism's encoder/decoder to siobuf. This is
+ done now, since subsequent commands and responses will be
+ encoded and remain so until the connection to the server
+ is closed. Auth_encode/decode() call through to the
+ mechanism's coders. */
+ sio_set_securitycb (conn, auth_encode, auth_decode,
+ session->auth_context);
+ session->auth_context = NULL; /* Don't permit AUTH */
+ session->extensions = 0; /* Turn off extensions */
+ session->rsp_state = S_ehlo; /* Restart protocol */
+ }
+#ifdef USE_ETRN
+ else if (check_etrn (session))
+ session->rsp_state = S_etrn;
+#endif
+ else
+ session->rsp_state = initial_transaction_state (session);
+ }
+ else if (code == 3)
+ session->rsp_state = S_auth2;
+ else
+ {
+ set_error (SMTP_ERR_INVALID_RESPONSE_STATUS);
+ session->rsp_state = S_quit;
+ }
+}
+
+void
+cmd_auth2 (siobuf_t conn, smtp_session_t session)
+{
+ char buf[2048];
+ const char *response;
+ int len;
+
+ /* Decode the text from the server to get the challenge. */
+ len = b64_decode (buf, sizeof buf, session->mta_status.text, -1);
+ if (len >= 0)
+ {
+ /* Send it through SASL and get the response. */
+ response = auth_response (session->auth_context, buf, &len);
+
+ /* Encode the response and send it back to the server */
+ len = (response != NULL) ? b64_encode (buf, sizeof buf, response, len)
+ : -1;
+ }
+
+ /* Abort the AUTH command if base 64 encode/decode fails. */
+ if (len < 0)
+ sio_write (conn, "*\r\n", 3);
+ else
+ {
+ if (len > 0)
+ sio_write (conn, buf, len);
+ sio_write (conn, "\r\n", 2);
+ }
+ session->cmd_state = -1;
+}
+
+void
+rsp_auth2 (siobuf_t conn, smtp_session_t session)
+{
+ rsp_auth (conn, session);
+}
+
+#else
+
+/* Define stubs for some of the SMTP AUTH support. */
+#include <stdlib.h>
+#include "auth-client.h"
+#include "libesmtp-private.h"
+
+int
+smtp_auth_set_context (smtp_session_t session, auth_context_t context)
+{
+ SMTPAPI_CHECK_ARGS (session != NULL, 0);
+
+ return 0;
+}
+
+void
+set_auth_mechanisms (smtp_session_t session, const char *mechanisms)
+{
+}
+
+int
+select_auth_mechanism (smtp_session_t session)
+{
+ return 0;
+}
+
+void
+destroy_auth_mechanisms (smtp_session_t session)
+{
+}
+
+#endif
diff --git a/smtp-bdat.c b/smtp-bdat.c
new file mode 100644
index 0000000..c9c77d9
--- /dev/null
+++ b/smtp-bdat.c
@@ -0,0 +1,314 @@
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* Support for the SMTP BDAT verb (CHUNKING). The code for processing
+ headers is duplicated from the DATA verb's code. Make sure to keep
+ bug fixes in sync with that code. Message body and response
+ processing is specific to the BDAT code and bears no resemblance to
+ that of the DATA command. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef USE_CHUNKING
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <missing.h> /* declarations for missing library functions */
+
+#include "libesmtp-private.h"
+#include "message-source.h"
+#include "siobuf.h"
+#include "concatenate.h"
+#include "headers.h"
+#include "protocol.h"
+
+/* Read the message from the application using the callback.
+ Break into chunks and copy to the server. */
+void
+cmd_bdat (siobuf_t conn, smtp_session_t session)
+{
+ const char *line, *header, *chunk;
+ int c, len;
+ struct catbuf headers;
+
+ sio_set_timeout (conn, session->transfer_timeout);
+
+ /* Arrange to read the current message from the application. */
+ msg_source_set_cb (session->msg_source,
+ session->current_message->cb,
+ session->current_message->cb_arg);
+
+ /* Arrange *not* to have the message contents monitored. This is
+ purely to avoid overwhelming the application with data. */
+ sio_set_monitorcb (conn, NULL, NULL);
+
+ /* Make sure we read the message from the beginning and get
+ the header processing right. */
+ msg_rewind (session->msg_source);
+ reset_header_table (session->current_message);
+
+ /* Initialise a buffer for the message headers. */
+ cat_init (&headers, 1024);
+
+ /* Read and process header lines from the application.
+ This step in processing
+ i) removes headers provided by the application that should not be
+ present in the message, e.g. Return-Path: which is added by
+ an MTA during delivery but should not be present in a message
+ being submitted or which is in transit.
+ ii) copies certain headers verbatim, e.g. MIME headers.
+ iii) alters the content of certain headers. This will happen
+ according to library options set up by the application.
+ */
+ errno = 0;
+ while ((line = msg_gets (session->msg_source, &len, 0)) != NULL)
+ {
+ /* Header processing stops at a line containing only CRLF */
+ if (len == 2 && line[0] == '\r' && line[1] == '\n')
+ break;
+
+ /* Check for continuation lines indicated by a blank or tab
+ at the start of the next line. */
+ while ((c = msg_nextc (session->msg_source)) != -1)
+ {
+ if (c != ' ' && c != '\t')
+ break;
+ line = msg_gets (session->msg_source, &len, 1);
+ if (line == NULL)
+ goto break_2;
+ }
+
+ /* Line points to one or more lines of text forming an RFC 2822
+ header. */
+
+ /* Header processing. This function takes the "raw" header from
+ the application and returns a header which is to be written
+ to the remote MTA. If header is NULL this header has been
+ deleted by the library. If header == line it is passed
+ unchanged otherwise, header must be freed after use. */
+ header = process_header (session->current_message, line, &len);
+ if (header != NULL)
+ {
+ /* Notify byte count to the application. */
+ if (session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_MESSAGEDATA,
+ session->event_cb_arg,
+ session->current_message, len);
+
+ /* During data transfer, if we are monitoring the message
+ headers, call the monitor callback directly, once per header.
+ We don't bother with monitoring the dot stuffing. Also set
+ the value of the writing parameter to 2 so that the app can
+ distinguish headers from data written in the sio_ package. */
+ if (session->monitor_cb && session->monitor_cb_headers)
+ (*session->monitor_cb) (header, len, SMTP_CB_HEADERS,
+ session->monitor_cb_arg);
+
+ /* Accumulate the header line into a buffer */
+ concatenate (&headers, header, len);
+ }
+ errno = 0;
+ }
+break_2:
+ if (errno != 0)
+ {
+ /* An error occurred during processing. The only thing that can
+ be done is to drop the connection to the server since SMTP has
+ no way to recover gracefully from client errors while transferring
+ the message. */
+ set_errno (errno);
+ session->cmd_state = session->rsp_state = -1;
+ return;
+ }
+
+ /* Now send missing headers. This completes the processing started
+ above. If the application did not supply certain headers that
+ should be present, the library will supply them here, e.g. Date:,
+ Message-Id: or To:/Cc:/Bcc: headers. In the most extreme case the
+ application might just send a CRLF followed by the message body.
+ Libesmtp will then provide all the necessary headers. */
+ while ((header = missing_header (session->current_message, &len)) != NULL)
+ {
+ /* Notify byte count to the application. */
+ if (session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_MESSAGEDATA,
+ session->event_cb_arg,
+ session->current_message, len);
+
+ if (session->monitor_cb && session->monitor_cb_headers)
+ (*session->monitor_cb) (header, len, SMTP_CB_HEADERS,
+ session->monitor_cb_arg);
+ /* Accumulate the header line into a buffer */
+ concatenate (&headers, header, len);
+ }
+
+ /* Terminate headers */
+ concatenate (&headers, "\r\n", 2);
+
+ /* ``headers'' now contains the message headers. Transfer them in a
+ BDAT command and move to the next state. */
+ session->bdat_abort_pipeline = 0;
+ session->bdat_last_issued = 0;
+ session->bdat_pipelined = 1;
+ chunk = cat_buffer (&headers, &len);
+ sio_printf (conn, "BDAT %d\r\n", len);
+ sio_write (conn, chunk, len);
+ cat_free (&headers);
+ session->cmd_state = S_bdat2;
+}
+
+void
+rsp_bdat (siobuf_t conn, smtp_session_t session)
+{
+ rsp_bdat2 (conn, session);
+}
+
+void
+cmd_bdat2 (siobuf_t conn, smtp_session_t session)
+{
+ const char *chunk;
+ int len;
+
+ /* N.B. the BDAT chunk size is set by the amount of buffering
+ provided by the application callback. An application is not
+ advised to read a message line by line in the callback.
+ Instead it should buffer the message by a "reasonable" amount,
+ say, 2Kb. */
+ errno = 0;
+ chunk = msg_getb (session->msg_source, &len);
+ if (chunk != NULL)
+ {
+ /* Notify byte count to the application. */
+ if (session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_MESSAGEDATA,
+ session->event_cb_arg,
+ session->current_message, len);
+ sio_printf (conn, "BDAT %d\r\n", len);
+ sio_write (conn, chunk, len);
+
+ /* BDAT commands may be pipelined. Check if a a previous BDAT has
+ failed and stop pipelining if necessary. */
+ session->cmd_state = session->bdat_abort_pipeline ? -1 : S_bdat2;
+ }
+ else
+ {
+ /* Workaround - M$ Exchange is broken wrt RFC 3030 */
+ if (session->extensions & EXT_XEXCH50)
+ sio_write (conn, "BDAT 2 LAST\r\n\r\n", -1);
+ else
+ sio_write (conn, "BDAT 0 LAST\r\n", -1);
+ sio_set_timeout (conn, session->data2_timeout);
+ session->bdat_last_issued = 1;
+ session->cmd_state = -1;
+ }
+ session->bdat_pipelined += 1;
+ if (errno != 0)
+ {
+ set_errno (errno);
+ session->cmd_state = session->rsp_state = -1;
+ }
+}
+
+void
+rsp_bdat2 (siobuf_t conn, smtp_session_t session)
+{
+ int code;
+ smtp_message_t message;
+ smtp_recipient_t recipient;
+
+ message = session->current_message;
+ code = read_smtp_response (conn, session, &message->message_status, NULL);
+
+ session->bdat_pipelined -= 1;
+ if (code == 2)
+ {
+ if (session->bdat_pipelined > 0 || !session->bdat_last_issued)
+ session->rsp_state = S_bdat2;
+ else
+ {
+ /* Mark all the recipients complete for which the MTA has accepted
+ responsibility for delivery. */
+ for (recipient = session->current_message->recipients;
+ recipient != NULL;
+ recipient = recipient->next)
+ if (!recipient->complete
+ && recipient->status.code >= 200
+ && recipient->status.code <= 299)
+ recipient->complete = 1;
+
+ /* Notify `message sent' */
+ if (session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_MESSAGESENT,
+ session->event_cb_arg,
+ session->current_message);
+
+ if (next_message (session))
+ session->rsp_state = initial_transaction_state (session);
+ else
+ session->rsp_state = S_quit;
+ }
+ }
+ else
+ {
+ /* Stop further BDAT commands from being issued. */
+ session->bdat_abort_pipeline = 1;
+
+ if (session->bdat_pipelined > 0)
+ session->rsp_state = S_bdat2;
+ else
+ {
+ /* Mark all the recipients complete. This message cannot be
+ accepted for any recipients. */
+ if (code == 5)
+ for (recipient = session->current_message->recipients;
+ recipient != NULL;
+ recipient = recipient->next)
+ recipient->complete = 1;
+
+ /* Notify `message sent' */
+ if (session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_MESSAGESENT,
+ session->event_cb_arg,
+ session->current_message);
+
+ /* RFC 3030 is explicit that when the BDAT command fails
+ the server state is indeterminate and that a RSET
+ command must be issued. If there are no more messages or
+ an unexpected status is received, just QUIT. */
+ if (!(code == 4 || code == 5))
+ {
+ set_error (SMTP_ERR_INVALID_RESPONSE_STATUS);
+ session->rsp_state = S_quit;
+ }
+ else if (next_message (session))
+ session->rsp_state = S_rset;
+ else
+ session->rsp_state = S_quit;
+ }
+ }
+}
+#endif
diff --git a/smtp-etrn.c b/smtp-etrn.c
new file mode 100644
index 0000000..ec8ca14
--- /dev/null
+++ b/smtp-etrn.c
@@ -0,0 +1,251 @@
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef USE_ETRN
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <missing.h> /* declarations for missing library functions */
+
+#include "libesmtp-private.h"
+
+#include "siobuf.h"
+#include "protocol.h"
+
+struct smtp_etrn_node
+ {
+ struct smtp_etrn_node *next;
+ struct smtp_session *session; /* Back reference */
+
+ /* Application data */
+ void *application_data; /* Pointer to data maintained by app */
+
+ /* Node Info */
+ int option; /* Option character */
+ char *domain; /* Remote queue to start */
+
+ /* Status */
+ smtp_status_t status; /* Status from MTA greeting */
+ };
+
+smtp_etrn_node_t
+smtp_etrn_add_node (smtp_session_t session, int option, const char *domain)
+{
+ smtp_etrn_node_t node;
+ char *dup_domain;
+
+ SMTPAPI_CHECK_ARGS (session != NULL
+ && domain != NULL
+ && (option == 0 || option == '@'), NULL);
+
+ if ((node = malloc (sizeof (struct smtp_etrn_node))) == NULL)
+ {
+ set_errno (ENOMEM);
+ return 0;
+ }
+ if ((dup_domain = strdup (domain)) == NULL)
+ {
+ free (node);
+ set_errno (ENOMEM);
+ return 0;
+ }
+
+ memset (node, 0, sizeof (struct smtp_etrn_node));
+ node->session = session;
+ node->option = option;
+ node->domain = dup_domain;
+ APPEND_LIST (session->etrn_nodes, session->end_etrn_nodes, node);
+ session->required_extensions |= EXT_ETRN;
+ return node;
+}
+
+int
+smtp_etrn_enumerate_nodes (smtp_session_t session,
+ smtp_etrn_enumerate_nodecb_t cb, void *arg)
+{
+ smtp_etrn_node_t node;
+
+ SMTPAPI_CHECK_ARGS (session != NULL && cb != NULL, 0);
+
+ for (node = session->etrn_nodes; node != NULL; node = node->next)
+ (*cb) (node, node->option, node->domain, arg);
+ return 1;
+}
+
+const smtp_status_t *
+smtp_etrn_node_status (smtp_etrn_node_t node)
+{
+ SMTPAPI_CHECK_ARGS (node != NULL, NULL);
+
+ return &node->status;
+}
+
+void *
+smtp_etrn_set_application_data (smtp_etrn_node_t node, void *data)
+{
+ void *old;
+
+ SMTPAPI_CHECK_ARGS (node != NULL, NULL);
+
+ old = node->application_data;
+ node->application_data = data;
+ return old;
+}
+
+void *
+smtp_etrn_get_application_data (smtp_etrn_node_t node)
+{
+ SMTPAPI_CHECK_ARGS (node != NULL, NULL);
+
+ return node->application_data;
+}
+
+int
+check_etrn (smtp_session_t session)
+{
+ return (session->extensions & EXT_ETRN) && session->etrn_nodes != NULL;
+}
+
+void
+destroy_etrn_nodes (smtp_session_t session)
+{
+ smtp_etrn_node_t node, next;
+
+ for (node = session->etrn_nodes; node != NULL; node = next)
+ {
+ next = node->next;
+ free (node->domain);
+ free (node);
+ }
+ session->etrn_nodes = session->end_etrn_nodes = NULL;
+ session->cmd_etrn_node = session->rsp_etrn_node = NULL;
+}
+
+void
+cmd_etrn (siobuf_t conn, smtp_session_t session)
+{
+ smtp_etrn_node_t node;
+
+ if (session->cmd_etrn_node == NULL)
+ session->cmd_etrn_node = session->etrn_nodes;
+ node = session->cmd_etrn_node;
+
+ sio_printf (conn, "ETRN %c%s\r\n",
+ node->option ? node->option : ' ', node->domain);
+
+ session->cmd_etrn_node = session->cmd_etrn_node->next;
+ if (session->cmd_etrn_node != NULL)
+ session->cmd_state = S_etrn;
+ else if (session->cmd_recipient != NULL)
+ session->cmd_state = initial_transaction_state (session);
+ else
+ session->cmd_state = S_quit;
+}
+
+void
+rsp_etrn (siobuf_t conn, smtp_session_t session)
+{
+ int code;
+ smtp_etrn_node_t node;
+
+ if (session->rsp_etrn_node == NULL)
+ session->rsp_etrn_node = session->etrn_nodes;
+ node = session->rsp_etrn_node;
+ code = read_smtp_response (conn, session, &node->status, NULL);
+ if (code < 0)
+ {
+ session->rsp_state = S_quit;
+ return;
+ }
+
+ /* Notify the ETRN status */
+ if (session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_ETRNSTATUS, session->event_cb_arg,
+ node->option, node->domain);
+
+ session->rsp_etrn_node = session->rsp_etrn_node->next;
+ if (session->rsp_etrn_node != NULL)
+ session->rsp_state = S_etrn;
+ else if (session->rsp_recipient != NULL)
+ session->rsp_state = initial_transaction_state (session);
+ else
+ session->rsp_state = S_quit;
+}
+
+#else
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include "libesmtp-private.h"
+
+smtp_etrn_node_t
+smtp_etrn_add_node (smtp_session_t session, int option, const char *domain)
+{
+ SMTPAPI_CHECK_ARGS (session != NULL
+ && domain != NULL
+ && (option == 0 || option == '@'), NULL);
+
+ return NULL;
+}
+
+int
+smtp_etrn_enumerate_nodes (smtp_session_t session,
+ smtp_etrn_enumerate_nodecb_t cb,
+ void *arg __attribute__ ((unused)))
+{
+ SMTPAPI_CHECK_ARGS (session != NULL && cb != NULL, 0);
+
+ return 0;
+}
+
+const smtp_status_t *
+smtp_etrn_node_status (smtp_etrn_node_t node)
+{
+ SMTPAPI_CHECK_ARGS (node != NULL, NULL);
+
+ return NULL;
+}
+
+void *
+smtp_etrn_set_application_data (smtp_etrn_node_t node,
+ void *data __attribute__ ((unused)))
+{
+ SMTPAPI_CHECK_ARGS (node != NULL, NULL);
+
+ return NULL;
+}
+
+void *
+smtp_etrn_get_application_data (smtp_etrn_node_t node)
+{
+ SMTPAPI_CHECK_ARGS (node != NULL, NULL);
+
+ return NULL;
+}
+
+#endif
diff --git a/smtp-tls.c b/smtp-tls.c
new file mode 100644
index 0000000..90adfb4
--- /dev/null
+++ b/smtp-tls.c
@@ -0,0 +1,715 @@
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001-2004 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* Support for the SMTP STARTTLS verb.
+ */
+
+
+#ifdef USE_TLS
+
+/* This stuff doesn't belong here */
+/* vvvvvvvvvvv */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <openssl/x509v3.h>
+#include <openssl/err.h>
+#include <missing.h> /* declarations for missing library functions */
+/* ^^^^^^^^^^^ */
+
+#include <ctype.h>
+#include "libesmtp-private.h"
+#include "siobuf.h"
+#include "protocol.h"
+
+
+static int tls_init;
+static SSL_CTX *starttls_ctx;
+static smtp_starttls_passwordcb_t ctx_password_cb;
+static void *ctx_password_cb_arg;
+
+
+#ifdef USE_PTHREADS
+#include <pthread.h>
+static pthread_mutex_t starttls_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t *openssl_mutex;
+
+static void
+openssl_mutexcb (int mode, int n,
+ const char *file __attribute__ ((unused)),
+ int line __attribute__ ((unused)))
+{
+ if (mode & CRYPTO_LOCK)
+ pthread_mutex_lock (&openssl_mutex[n]);
+ else
+ pthread_mutex_unlock (&openssl_mutex[n]);
+}
+#endif
+
+static int
+starttls_init (void)
+{
+ if (tls_init)
+ return 1;
+
+#ifdef USE_PTHREADS
+ /* Set up mutexes for the OpenSSL library */
+ if (openssl_mutex == NULL)
+ {
+ pthread_mutexattr_t attr;
+ int n;
+
+ openssl_mutex = malloc (sizeof (pthread_mutex_t) * CRYPTO_num_locks ());
+ if (openssl_mutex == NULL)
+ return 0;
+ pthread_mutexattr_init (&attr);
+ for (n = 0; n < CRYPTO_num_locks (); n++)
+ pthread_mutex_init (&openssl_mutex[n], &attr);
+ pthread_mutexattr_destroy (&attr);
+ CRYPTO_set_locking_callback (openssl_mutexcb);
+ }
+#endif
+ tls_init = 1;
+ SSL_load_error_strings ();
+ SSL_library_init ();
+ return 1;
+}
+
+/* This stuff is crude and doesn't belong here */
+/* vvvvvvvvvvv */
+
+static const char *
+get_home (void)
+{
+ return getenv ("HOME");
+}
+
+static char *
+user_pathname (char buf[], size_t buflen, const char *tail)
+{
+ const char *home;
+
+ home = get_home ();
+ snprintf (buf, buflen, "%s/.authenticate/%s", home, tail);
+ return buf;
+}
+
+typedef enum { FILE_PROBLEM, FILE_NOT_PRESENT, FILE_OK } ckf_t;
+
+/* Check file exists, is a regular file and contains something */
+static ckf_t
+check_file (const char *file)
+{
+ struct stat st;
+
+ errno = 0;
+ if (stat (file, &st) < 0)
+ return (errno == ENOENT) ? FILE_NOT_PRESENT : FILE_PROBLEM;
+ /* File must be regular and contain something */
+ if (!(S_ISREG (st.st_mode) && st.st_size > 0))
+ return FILE_PROBLEM;
+ /* For now this check is paranoid. The way I figure it, the passwords
+ on the private keys will be intensely annoying, so people will
+ remove them. Therefore make them protect the files using very
+ restrictive file permissions. Only the owner should be able to
+ read or write the certificates and the user the app is running as
+ should own the files. */
+ if ((st.st_mode & (S_IXUSR | S_IRWXG | S_IRWXO)) || st.st_uid != getuid ())
+ return FILE_PROBLEM;
+ return FILE_OK;
+}
+
+/* Check directory exists */
+static ckf_t
+check_directory (const char *file)
+{
+ struct stat st;
+
+ if (stat (file, &st) < 0)
+ return (errno == ENOENT) ? FILE_NOT_PRESENT : FILE_PROBLEM;
+ /* File must be a directory */
+ if (!S_ISDIR (st.st_mode))
+ return FILE_PROBLEM;
+ /* Paranoia - only permit owner rwx permissions */
+ if ((st.st_mode & (S_IRWXG | S_IRWXO)) || st.st_uid != getuid ())
+ return FILE_PROBLEM;
+ return FILE_OK;
+}
+
+/* ^^^^^^^^^^^ */
+
+/* Unusually this API does not require a smtp_session_t. The data
+ it sets is global.
+
+ N.B. If this API is not called and OpenSSL requires a password, it
+ will supply a default callback which prompts on the user's tty.
+ This is likely to be undesired behaviour, so the app should
+ supply a callback using this function.
+ */
+int
+smtp_starttls_set_password_cb (smtp_starttls_passwordcb_t cb, void *arg)
+{
+ SMTPAPI_CHECK_ARGS (cb != NULL, 0);
+
+#ifdef USE_PTHREADS
+ pthread_mutex_lock (&starttls_mutex);
+#endif
+ ctx_password_cb = cb;
+ ctx_password_cb_arg = arg;
+#ifdef USE_PTHREADS
+ pthread_mutex_unlock (&starttls_mutex);
+#endif
+ return 1;
+}
+
+static SSL_CTX *
+starttls_create_ctx (smtp_session_t session)
+{
+ SSL_CTX *ctx;
+ char buf[2048];
+ char buf2[2048];
+ char *keyfile, *cafile, *capath;
+ ckf_t status;
+
+ /* The decision not to support SSL v2 and v3 but instead to use only
+ TLSv1 is deliberate. This is in line with the intentions of RFC
+ 3207. Servers typically support SSL as well as TLS because some
+ versions of Netscape do not support TLS. I am assuming that all
+ currently deployed servers correctly support TLS. */
+ ctx = SSL_CTX_new (TLSv1_client_method ());
+
+ /* Load our keys and certificates. To avoid messing with configuration
+ variables etc, use fixed paths for the certificate store. These are
+ as follows :-
+
+ ~/.authenticate/private/smtp-starttls.pem
+ the user's certificate and private key
+ ~/.authenticate/ca.pem
+ the user's trusted CA list
+ ~/.authenticate/ca
+ the user's hashed CA directory
+
+ Host specific stuff follows the same structure except that its
+ below ~/.authenticate/host.name
+
+ It probably makes sense to check that the directories and/or files
+ are readable only by the user who owns them.
+
+ This structure will certainly change. I'm on a voyage of discovery
+ here! Eventually, this code and the SASL stuff will become a
+ separate library used by libESMTP (libAUTH?). The general idea is
+ that ~/.authenticate will be used to store authentication
+ information for the user (eventually there might be a
+ ({/usr{/local}}/etc/authenticate for system wide stuff - CA lists
+ and CRLs for example). The "private" subdirectory is just to
+ separate out private info from others. There might be a "public"
+ directory too. Since the CA list is global (I think) I've put them
+ below .authenticate for now. Within the "private" and "public"
+ directories, certificates and other authentication data are named
+ according to their purpose (hence smtp-starttls.pem). Symlinks can
+ be used to avoid duplication where authentication tokens are shared
+ for several purposes. My reasoning here is that libESMTP (or any
+ client layered over the hypothetical libAUTH) will always want the
+ same authentication behaviour for a given service, regardless of
+ the application using it.
+
+ XXX - The above isn't quite enough. Per-host directories are
+ required, e.g. a different smtp-starttls.pem might be needed for
+ different servers. This will not affect the trusted CAs though.
+
+ XXX - (this comment belongs in auth-client.c) Ideally, the
+ ~/.authenticate hierarchy will be able to store SASL passwords
+ if required, preferably encrypted. Then the application would
+ not necessarily have to supply usernames and passwords via the
+ libESMTP API to be able to authenticate to a server.
+ */
+
+ /* Client certificate policy: if a client certificate is found at
+ ~/.authenticate/private/smtp-starttls.pem, it is presented to the
+ server if it requests it. The server may use the certificate to
+ perform authentication at its own discretion. */
+ if (ctx_password_cb != NULL)
+ {
+ SSL_CTX_set_default_passwd_cb (ctx, ctx_password_cb);
+ SSL_CTX_set_default_passwd_cb_userdata (ctx, ctx_password_cb_arg);
+ }
+ keyfile = user_pathname (buf, sizeof buf, "private/smtp-starttls.pem");
+ status = check_file (keyfile);
+ if (status == FILE_OK)
+ {
+ if (!SSL_CTX_use_certificate_file (ctx, keyfile, SSL_FILETYPE_PEM))
+ {
+ /* FIXME: set an error code */
+ return NULL;
+ }
+ if (!SSL_CTX_use_PrivateKey_file (ctx, keyfile, SSL_FILETYPE_PEM))
+ {
+ int ok = 0;
+ if (session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_NO_CLIENT_CERTIFICATE,
+ session->event_cb_arg, &ok);
+ if (!ok)
+ return NULL;
+ }
+ }
+ else if (status == FILE_PROBLEM)
+ {
+ if (session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_UNUSABLE_CLIENT_CERTIFICATE,
+ session->event_cb_arg, NULL);
+ return NULL;
+ }
+
+ /* Server certificate policy: check the server certificate against the
+ trusted CA list to a depth of 1. */
+ cafile = user_pathname (buf, sizeof buf, "ca.pem");
+ status = check_file (cafile);
+ if (status == FILE_NOT_PRESENT)
+ cafile = NULL;
+ else if (status == FILE_PROBLEM)
+ {
+ if (session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_UNUSABLE_CA_LIST,
+ session->event_cb_arg, NULL);
+ return NULL;
+ }
+ capath = user_pathname (buf2, sizeof buf2, "ca");
+ status = check_directory (capath);
+ if (status == FILE_NOT_PRESENT)
+ capath = NULL;
+ else if (status == FILE_PROBLEM)
+ {
+ if (session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_UNUSABLE_CA_LIST,
+ session->event_cb_arg, NULL);
+ return NULL;
+ }
+
+ /* Load the CAs we trust */
+ if (cafile != NULL || capath != NULL)
+ SSL_CTX_load_verify_locations (ctx, cafile, capath);
+ else
+ SSL_CTX_set_default_verify_paths (ctx);
+
+ /* FIXME: load a source of randomness */
+
+ return ctx;
+}
+
+static SSL *
+starttls_create_ssl (smtp_session_t session)
+{
+ char buf[2048];
+ char buf2[2048];
+ char *keyfile;
+ SSL *ssl;
+ ckf_t status;
+
+ ssl = SSL_new (session->starttls_ctx);
+
+ /* Client certificate policy: if a host specific client certificate
+ is found at ~/.authenticate/host.name/private/smtp-starttls.pem,
+ it is presented to the server if it requests it. */
+
+ /* FIXME: when the default client certificate is loaded a passowrd may be
+ required. Then the code below might ask for one too. It
+ will be annoying when two passwords are needed and only one
+ is necessary. Also, the default certificate will only need
+ the password when the SSL_CTX is created. A host specific
+ certificate's password will be needed for every SMTP session
+ within an application. This needs a solution. */
+
+ /* FIXME: in protocol.c, record the canonic name of the host returned
+ by getaddrinfo. Use that instead of session->host. */
+ snprintf (buf2, sizeof buf2, "%s/private/smtp-starttls.pem", session->host);
+ keyfile = user_pathname (buf, sizeof buf, buf2);
+ status = check_file (keyfile);
+ if (status == FILE_OK)
+ {
+ if (!SSL_use_certificate_file (ssl, keyfile, SSL_FILETYPE_PEM))
+ {
+ /* FIXME: set an error code */
+ return NULL;
+ }
+ if (!SSL_use_PrivateKey_file (ssl, keyfile, SSL_FILETYPE_PEM))
+ {
+ int ok = 0;
+ if (session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_NO_CLIENT_CERTIFICATE,
+ session->event_cb_arg, &ok);
+ if (!ok)
+ return NULL;
+ }
+ }
+ else if (status == FILE_PROBLEM)
+ {
+ if (session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_UNUSABLE_CLIENT_CERTIFICATE,
+ session->event_cb_arg, NULL);
+ return NULL;
+ }
+
+ return ssl;
+}
+
+/* App calls this to allow libESMTP to use an SSL_CTX it has already
+ initialised. NULL means use a default created by libESMTP.
+ If called at all, libESMTP assumes the application has initialised
+ openssl. Otherwise, libESMTP will initialise OpenSSL before calling
+ any of the SSL APIs. */
+int
+smtp_starttls_set_ctx (smtp_session_t session, SSL_CTX *ctx)
+{
+ SMTPAPI_CHECK_ARGS (session != NULL, 0);
+
+ tls_init = 1; /* Assume app has set up openssl */
+ session->starttls_ctx = ctx;
+ return 1;
+}
+
+/* how == 0: disabled, 1: if possible, 2: required */
+int
+smtp_starttls_enable (smtp_session_t session, enum starttls_option how)
+{
+ SMTPAPI_CHECK_ARGS (session != NULL, 0);
+
+ session->starttls_enabled = how;
+ if (how == Starttls_REQUIRED)
+ session->required_extensions |= EXT_STARTTLS;
+ else
+ session->required_extensions &= ~EXT_STARTTLS;
+ return 1;
+}
+
+int
+select_starttls (smtp_session_t session)
+{
+ if (session->using_tls || session->authenticated)
+ return 0;
+ /* FIXME: if the server has reported the TLS extension in a previous
+ session promote Starttls_ENABLED to Starttls_REQUIRED.
+ If this session does not offer STARTTLS, this will force
+ protocol.c to report the extension as not available and QUIT
+ as reccommended in RFC 3207. This requires some form of db
+ storage to record this for future sessions. */
+ /* if (...)
+ session->starttls_enabled = Starttls_REQUIRED; */
+ if (!(session->extensions & EXT_STARTTLS))
+ return 0;
+ if (!session->starttls_enabled)
+ return 0;
+#ifdef USE_PTHREADS
+ pthread_mutex_lock (&starttls_mutex);
+#endif
+ if (starttls_ctx == NULL && starttls_init ())
+ starttls_ctx = starttls_create_ctx (session);
+#ifdef USE_PTHREADS
+ pthread_mutex_unlock (&starttls_mutex);
+#endif
+ session->starttls_ctx = starttls_ctx;
+ return session->starttls_ctx != NULL;
+}
+
+static int
+match_component (const char *dom, const char *edom,
+ const char *ref, const char *eref)
+{
+ while (dom < edom && ref < eref)
+ {
+ /* Accept a final '*' in the reference as a wildcard */
+ if (*ref == '*' && ref + 1 == eref)
+ break;
+ /* compare the domain name case insensitive */
+ if (!(*dom == *ref || tolower (*dom) == tolower (*ref)))
+ return 0;
+ ref++, dom++;
+ }
+ return 1;
+}
+
+/* Perform a domain name comparison where the reference may contain
+ wildcards. This implements the comparison from RFC 2818.
+ Each component of the domain name is matched separately, working from
+ right to left.
+ */
+static int
+match_domain (const char *domain, const char *reference)
+{
+ const char *dom, *edom, *ref, *eref;
+
+ eref = strchr (reference, '\0');
+ edom = strchr (domain, '\0');
+ while (eref > reference && edom > domain)
+ {
+ /* Find the rightmost component of the reference. */
+ ref = memrchr (reference, '.', eref - reference - 1);
+ if (ref != NULL)
+ ref++;
+ else
+ ref = reference;
+
+ /* Find the rightmost component of the domain name. */
+ dom = memrchr (domain, '.', edom - domain - 1);
+ if (dom != NULL)
+ dom++;
+ else
+ dom = domain;
+
+ if (!match_component (dom, edom, ref, eref))
+ return 0;
+ edom = dom - 1;
+ eref = ref - 1;
+ }
+ return eref < reference && edom < domain;
+}
+
+static int
+check_acceptable_security (smtp_session_t session, SSL *ssl)
+{
+ X509 *cert;
+ char buf[256];
+ int bits;
+ long vfy_result;
+ int ok;
+
+ /* Check certificate validity.
+ */
+ vfy_result = SSL_get_verify_result (ssl);
+ if (vfy_result != X509_V_OK)
+ {
+ ok = 0;
+ if (session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_INVALID_PEER_CERTIFICATE,
+ session->event_cb_arg, vfy_result, &ok, ssl);
+ if (!ok)
+ return 0;
+#if 0
+ /* Not sure about the location of this call so leave it out for now
+ - from Pawel: the worst thing that can happen is that one can
+ get non-empty error log in wrong places. */
+ ERR_clear_error (); /* we know what is going on, clear the error log */
+#endif
+ }
+
+ /* Check cipher strength. Since we use only TLSv1 the cipher should
+ never be this weak. */
+ bits = SSL_get_cipher_bits (ssl, NULL);
+ if (bits <= 40)
+ {
+ ok = 0;
+ if (session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_WEAK_CIPHER,
+ session->event_cb_arg, bits, &ok);
+ if (!ok)
+ return 0;
+ }
+
+ /* Check server credentials stored in the certificate.
+ */
+ ok = 0;
+ cert = SSL_get_peer_certificate (ssl);
+ if (cert == NULL)
+ {
+ if (session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_NO_PEER_CERTIFICATE,
+ session->event_cb_arg, &ok);
+ }
+ else
+ {
+ int i, j, extcount;
+
+ extcount = X509_get_ext_count (cert);
+ for (i = 0; i < extcount; i++)
+ {
+ const char *extstr;
+ X509_EXTENSION *ext = X509_get_ext (cert, i);
+
+ extstr = OBJ_nid2sn (OBJ_obj2nid (X509_EXTENSION_get_object (ext)));
+ if (strcmp (extstr, "subjectAltName") == 0)
+ {
+ unsigned char *data;
+ STACK_OF(CONF_VALUE) *val;
+ CONF_VALUE *nval;
+ X509V3_EXT_METHOD *meth;
+ void *ext_str = NULL;
+ int stack_len;
+
+ meth = X509V3_EXT_get (ext);
+ if (meth == NULL)
+ break;
+ data = ext->value->data;
+#if (OPENSSL_VERSION_NUMBER > 0x00907000L)
+ if (meth->it)
+ ext_str = ASN1_item_d2i (NULL, &data, ext->value->length,
+ ASN1_ITEM_ptr (meth->it));
+ else
+#endif
+ ext_str = meth->d2i (NULL, &data, ext->value->length);
+ val = meth->i2v (meth, ext_str, NULL);
+ stack_len = sk_CONF_VALUE_num (val);
+ for (j = 0; j < stack_len; j++)
+ {
+ nval = sk_CONF_VALUE_value (val, j);
+ if (strcmp (nval->name, "DNS") == 0
+ && match_domain (session->host, nval->value))
+ {
+ ok = 1;
+ break;
+ }
+ }
+ }
+ if (ok)
+ break;
+ }
+ if (!ok)
+ {
+ /* Matching by subjectAltName failed, try commonName */
+ X509_NAME_get_text_by_NID (X509_get_subject_name (cert),
+ NID_commonName, buf, sizeof buf);
+ if (!match_domain (session->host, buf) != 0)
+ {
+ if (session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_WRONG_PEER_CERTIFICATE,
+ session->event_cb_arg, &ok, buf, ssl);
+ }
+ else
+ ok = 1;
+ }
+ X509_free (cert);
+ }
+ return ok;
+}
+
+void
+cmd_starttls (siobuf_t conn, smtp_session_t session)
+{
+ sio_write (conn, "STARTTLS\r\n", -1);
+ session->cmd_state = -1;
+}
+
+void
+rsp_starttls (siobuf_t conn, smtp_session_t session)
+{
+ int code;
+ SSL *ssl;
+ X509 *cert;
+ char buf[256];
+
+ code = read_smtp_response (conn, session, &session->mta_status, NULL);
+ if (code < 0)
+ {
+ session->rsp_state = S_quit;
+ return;
+ }
+
+ if (code != 2)
+ {
+ if (code != 4 && code != 5)
+ set_error (SMTP_ERR_INVALID_RESPONSE_STATUS);
+ session->rsp_state = S_quit;
+ }
+ else if (sio_set_tlsclient_ssl (conn, (ssl = starttls_create_ssl (session))))
+ {
+ session->using_tls = 1;
+
+ /* Forget what we know about the server and reset protocol state.
+ */
+ session->extensions = 0;
+ destroy_auth_mechanisms (session);
+
+ if (!check_acceptable_security (session, ssl))
+ session->rsp_state = S_quit;
+ else
+ {
+ if (session->event_cb != NULL)
+ (*session->event_cb) (session, SMTP_EV_STARTTLS_OK,
+ session->event_cb_arg,
+ ssl, SSL_get_cipher (ssl),
+ SSL_get_cipher_bits (ssl, NULL));
+ cert = SSL_get_certificate (ssl);
+ if (cert != NULL)
+ {
+ /* Copy the common name [typically email address] from the
+ client certificate and use it to prime the SASL EXTERNAL
+ mechanism */
+ X509_NAME_get_text_by_NID (X509_get_subject_name (cert),
+ NID_commonName, buf, sizeof buf);
+ X509_free (cert);
+ if (session->auth_context != NULL)
+ auth_set_external_id (session->auth_context, buf);
+ }
+
+ /* Next state is EHLO */
+ session->rsp_state = S_ehlo;
+ }
+ }
+ else
+ {
+ set_error (SMTP_ERR_CLIENT_ERROR);
+ session->rsp_state = -1;
+ }
+}
+
+#else
+
+#define SSL_CTX void
+#include "libesmtp-private.h"
+
+/* Fudge the declaration. The idea is that all builds of the library
+ export the same API, but that the unsupported features always fail.
+ This prototype is declared only if <openssl/ssl.h> is included.
+ Strict ANSI compiles require prototypes, so here it is! */
+int smtp_starttls_set_ctx (smtp_session_t session, SSL_CTX *ctx);
+
+int
+smtp_starttls_set_ctx (smtp_session_t session,
+ SSL_CTX *ctx __attribute__ ((unused)))
+{
+ SMTPAPI_CHECK_ARGS (session != (smtp_session_t) 0, 0);
+
+ return 0;
+}
+
+int
+smtp_starttls_enable (smtp_session_t session,
+ enum starttls_option how __attribute__ ((unused)))
+{
+ SMTPAPI_CHECK_ARGS (session != (smtp_session_t) 0, 0);
+
+ return 0;
+}
+
+int
+smtp_starttls_set_password_cb (smtp_starttls_passwordcb_t cb
+ __attribute__ ((unused)),
+ void *arg __attribute__ ((unused)))
+{
+ return 0;
+}
+
+#endif
diff --git a/snprintf.c b/snprintf.c
new file mode 100644
index 0000000..bd70741
--- /dev/null
+++ b/snprintf.c
@@ -0,0 +1,797 @@
+/**************************************************************
+ * Original:
+ * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
+ * A bombproof version of doprnt (dopr) included.
+ * Sigh. This sort of thing is always nasty do deal with. Note that
+ * the version here does not include floating point...
+ *
+ * snprintf() is used instead of sprintf() as it does limit checks
+ * for string length. This covers a nasty loophole.
+ *
+ * The other functions are there to prevent NULL pointers from
+ * causing nast effects.
+ *
+ * More Recently:
+ * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
+ * This was ugly. It is still ugly. I opted out of floating point
+ * numbers, but the formatter understands just about everything
+ * from the normal C string format, at least as far as I can tell from
+ * the Solaris 2.5 printf(3S) man page.
+ *
+ * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
+ * Ok, added some minimal floating point support, which means this
+ * probably requires libm on most operating systems. Don't yet
+ * support the exponent (e,E) and sigfig (g,G). Also, fmtint()
+ * was pretty badly broken, it just wasn't being exercised in ways
+ * which showed it, so that's been fixed. Also, formated the code
+ * to mutt conventions, and removed dead code left over from the
+ * original. Also, there is now a builtin-test, just compile with:
+ * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
+ * and run snprintf for results.
+ *
+ * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
+ * The PGP code was using unsigned hexadecimal formats.
+ * Unfortunately, unsigned formats simply didn't work.
+ *
+ * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
+ * The original code assumed that both snprintf() and vsnprintf() were
+ * missing. Some systems only have snprintf() but not vsnprintf(), so
+ * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
+ *
+ * Brian Stafford <brian@stafford.uklinux.net> 2002/01/28
+ * Removed calls to va_arg(ap, [unsigned] short); etc. This is because
+ * shorts are promoted to int when passed in a ... function argument and
+ * should be referred to as va_arg(ap, [unsigned] int); Added const to
+ * string argument types - snprintf does not modify the strings.
+ **************************************************************/
+
+#ifdef HAVE_CONFIG
+#include <config.h>
+#endif
+#undef _GNU_SOURCE
+#undef _ISOC9X_SOURCE
+#undef _OSF_SOURCE
+#undef _XOPEN_SOURCE
+#undef __EXTENSIONS__
+#include <missing.h>
+
+#if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF)
+
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+
+/* Define this as a fall through, HAVE_STDARG_H is probably already set */
+
+#ifdef __STDC__
+#define HAVE_STDARG_H
+#else
+#define HAVE_VARARGS_H
+#endif
+
+/* varargs declarations: */
+
+#if defined(HAVE_STDARG_H)
+# include <stdarg.h>
+# define HAVE_STDARGS /* let's hope that works everywhere (mj) */
+# define VA_LOCAL_DECL va_list ap
+# define VA_START(f) va_start(ap, f)
+# define VA_SHIFT(v,t) ; /* no-op for ANSI */
+# define VA_END va_end(ap)
+#else
+# if defined(HAVE_VARARGS_H)
+# include <varargs.h>
+# undef HAVE_STDARGS
+# define VA_LOCAL_DECL va_list ap
+# define VA_START(f) va_start(ap) /* f is ignored! */
+# define VA_SHIFT(v,t) v = va_arg(ap,t)
+# define VA_END va_end(ap)
+# else
+/*XX ** NO VARARGS ** XX*/
+# endif
+#endif
+
+/*int snprintf (char *str, size_t count, const char *fmt, ...);*/
+/*int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);*/
+
+static void dopr (char *buffer, size_t maxlen, const char *format,
+ va_list args);
+static void fmtstr (char *buffer, size_t *currlen, size_t maxlen,
+ const char *value, int flags, int min, int max);
+static void fmtint (char *buffer, size_t *currlen, size_t maxlen,
+ long value, int base, int min, int max, int flags);
+static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
+ long double fvalue, int min, int max, int flags);
+static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c );
+
+/*
+ * dopr(): poor man's version of doprintf
+ */
+
+/* format read states */
+#define DP_S_DEFAULT 0
+#define DP_S_FLAGS 1
+#define DP_S_MIN 2
+#define DP_S_DOT 3
+#define DP_S_MAX 4
+#define DP_S_MOD 5
+#define DP_S_CONV 6
+#define DP_S_DONE 7
+
+/* format flags - Bits */
+#define DP_F_MINUS (1 << 0)
+#define DP_F_PLUS (1 << 1)
+#define DP_F_SPACE (1 << 2)
+#define DP_F_NUM (1 << 3)
+#define DP_F_ZERO (1 << 4)
+#define DP_F_UP (1 << 5)
+#define DP_F_UNSIGNED (1 << 6)
+
+/* Conversion Flags */
+#define DP_C_SHORT 1
+#define DP_C_LONG 2
+#define DP_C_LDOUBLE 3
+
+#define char_to_int(p) (p - '0')
+#define MAX(p,q) ((p >= q) ? p : q)
+
+static void dopr (char *buffer, size_t maxlen, const char *format, va_list args)
+{
+ char ch;
+ long value;
+ long double fvalue;
+ const char *strvalue;
+ int min;
+ int max;
+ int state;
+ int flags;
+ int cflags;
+ size_t currlen;
+
+ state = DP_S_DEFAULT;
+ currlen = flags = cflags = min = 0;
+ max = -1;
+ ch = *format++;
+
+ while (state != DP_S_DONE)
+ {
+ if ((ch == '\0') || (currlen >= maxlen))
+ state = DP_S_DONE;
+
+ switch(state)
+ {
+ case DP_S_DEFAULT:
+ if (ch == '%')
+ state = DP_S_FLAGS;
+ else
+ dopr_outch (buffer, &currlen, maxlen, ch);
+ ch = *format++;
+ break;
+ case DP_S_FLAGS:
+ switch (ch)
+ {
+ case '-':
+ flags |= DP_F_MINUS;
+ ch = *format++;
+ break;
+ case '+':
+ flags |= DP_F_PLUS;
+ ch = *format++;
+ break;
+ case ' ':
+ flags |= DP_F_SPACE;
+ ch = *format++;
+ break;
+ case '#':
+ flags |= DP_F_NUM;
+ ch = *format++;
+ break;
+ case '0':
+ flags |= DP_F_ZERO;
+ ch = *format++;
+ break;
+ default:
+ state = DP_S_MIN;
+ break;
+ }
+ break;
+ case DP_S_MIN:
+ if (isdigit((unsigned char)ch))
+ {
+ min = 10*min + char_to_int (ch);
+ ch = *format++;
+ }
+ else if (ch == '*')
+ {
+ min = va_arg (args, int);
+ ch = *format++;
+ state = DP_S_DOT;
+ }
+ else
+ state = DP_S_DOT;
+ break;
+ case DP_S_DOT:
+ if (ch == '.')
+ {
+ state = DP_S_MAX;
+ ch = *format++;
+ }
+ else
+ state = DP_S_MOD;
+ break;
+ case DP_S_MAX:
+ if (isdigit((unsigned char)ch))
+ {
+ if (max < 0)
+ max = 0;
+ max = 10*max + char_to_int (ch);
+ ch = *format++;
+ }
+ else if (ch == '*')
+ {
+ max = va_arg (args, int);
+ ch = *format++;
+ state = DP_S_MOD;
+ }
+ else
+ state = DP_S_MOD;
+ break;
+ case DP_S_MOD:
+ /* Currently, we don't support Long Long, bummer */
+ switch (ch)
+ {
+ case 'h':
+ cflags = DP_C_SHORT;
+ ch = *format++;
+ break;
+ case 'l':
+ cflags = DP_C_LONG;
+ ch = *format++;
+ break;
+ case 'L':
+ cflags = DP_C_LDOUBLE;
+ ch = *format++;
+ break;
+ default:
+ break;
+ }
+ state = DP_S_CONV;
+ break;
+ case DP_S_CONV:
+ switch (ch)
+ {
+ case 'd':
+ case 'i':
+ if (cflags == DP_C_LONG)
+ value = va_arg (args, long int);
+ else
+ value = va_arg (args, int);
+ fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
+ break;
+ case 'o':
+ flags |= DP_F_UNSIGNED;
+ if (cflags == DP_C_LONG)
+ value = va_arg (args, unsigned long int);
+ else
+ value = va_arg (args, unsigned int);
+ fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
+ break;
+ case 'u':
+ flags |= DP_F_UNSIGNED;
+ if (cflags == DP_C_LONG)
+ value = va_arg (args, unsigned long int);
+ else
+ value = va_arg (args, unsigned int);
+ fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
+ break;
+ case 'X':
+ flags |= DP_F_UP;
+ case 'x':
+ flags |= DP_F_UNSIGNED;
+ if (cflags == DP_C_LONG)
+ value = va_arg (args, unsigned long int);
+ else
+ value = va_arg (args, unsigned int);
+ fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
+ break;
+ case 'f':
+ if (cflags == DP_C_LDOUBLE)
+ fvalue = va_arg (args, long double);
+ else
+ fvalue = va_arg (args, double);
+ /* um, floating point? */
+ fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
+ break;
+ case 'E':
+ flags |= DP_F_UP;
+ case 'e':
+ if (cflags == DP_C_LDOUBLE)
+ fvalue = va_arg (args, long double);
+ else
+ fvalue = va_arg (args, double);
+ break;
+ case 'G':
+ flags |= DP_F_UP;
+ case 'g':
+ if (cflags == DP_C_LDOUBLE)
+ fvalue = va_arg (args, long double);
+ else
+ fvalue = va_arg (args, double);
+ break;
+ case 'c':
+ dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
+ break;
+ case 's':
+ strvalue = va_arg (args, const char *);
+ if (max < 0)
+ max = maxlen; /* ie, no max */
+ fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
+ break;
+ case 'p':
+ strvalue = va_arg (args, const void *);
+ fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
+ break;
+ case 'n':
+ if (cflags == DP_C_SHORT)
+ {
+ short int *num;
+ num = va_arg (args, short int *);
+ *num = currlen;
+ }
+ else if (cflags == DP_C_LONG)
+ {
+ long int *num;
+ num = va_arg (args, long int *);
+ *num = currlen;
+ }
+ else
+ {
+ int *num;
+ num = va_arg (args, int *);
+ *num = currlen;
+ }
+ break;
+ case '%':
+ dopr_outch (buffer, &currlen, maxlen, ch);
+ break;
+ case 'w':
+ /* not supported yet, treat as next char */
+ ch = *format++;
+ break;
+ default:
+ /* Unknown, skip */
+ break;
+ }
+ ch = *format++;
+ state = DP_S_DEFAULT;
+ flags = cflags = min = 0;
+ max = -1;
+ break;
+ case DP_S_DONE:
+ break;
+ default:
+ /* hmm? */
+ break; /* some picky compilers need this */
+ }
+ }
+ if (currlen < maxlen - 1)
+ buffer[currlen] = '\0';
+ else
+ buffer[maxlen - 1] = '\0';
+}
+
+static void fmtstr (char *buffer, size_t *currlen, size_t maxlen,
+ const char *value, int flags, int min, int max)
+{
+ int padlen, strln; /* amount to pad */
+ int cnt = 0;
+
+ if (value == 0)
+ {
+ value = "<NULL>";
+ }
+
+ for (strln = 0; value[strln]; ++strln); /* strlen */
+ padlen = min - strln;
+ if (padlen < 0)
+ padlen = 0;
+ if (flags & DP_F_MINUS)
+ padlen = -padlen; /* Left Justify */
+
+ while ((padlen > 0) && (cnt < max))
+ {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ --padlen;
+ ++cnt;
+ }
+ while (*value && (cnt < max))
+ {
+ dopr_outch (buffer, currlen, maxlen, *value++);
+ ++cnt;
+ }
+ while ((padlen < 0) && (cnt < max))
+ {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ ++padlen;
+ ++cnt;
+ }
+}
+
+/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
+
+static void fmtint (char *buffer, size_t *currlen, size_t maxlen,
+ long value, int base, int min, int max, int flags)
+{
+ int signvalue = 0;
+ unsigned long uvalue;
+ char convert[20];
+ int place = 0;
+ int spadlen = 0; /* amount to space pad */
+ int zpadlen = 0; /* amount to zero pad */
+ int caps = 0;
+
+ if (max < 0)
+ max = 0;
+
+ uvalue = value;
+
+ if(!(flags & DP_F_UNSIGNED))
+ {
+ if( value < 0 ) {
+ signvalue = '-';
+ uvalue = -value;
+ }
+ else
+ if (flags & DP_F_PLUS) /* Do a sign (+/i) */
+ signvalue = '+';
+ else
+ if (flags & DP_F_SPACE)
+ signvalue = ' ';
+ }
+
+ if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
+
+ do {
+ convert[place++] =
+ (caps? "0123456789ABCDEF":"0123456789abcdef")
+ [uvalue % (unsigned)base ];
+ uvalue = (uvalue / (unsigned)base );
+ } while(uvalue && (place < 20));
+ if (place == 20) place--;
+ convert[place] = 0;
+
+ zpadlen = max - place;
+ spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
+ if (zpadlen < 0) zpadlen = 0;
+ if (spadlen < 0) spadlen = 0;
+ if (flags & DP_F_ZERO)
+ {
+ zpadlen = MAX(zpadlen, spadlen);
+ spadlen = 0;
+ }
+ if (flags & DP_F_MINUS)
+ spadlen = -spadlen; /* Left Justifty */
+
+#ifdef DEBUG_SNPRINTF
+ dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
+ zpadlen, spadlen, min, max, place));
+#endif
+
+ /* Spaces */
+ while (spadlen > 0)
+ {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ --spadlen;
+ }
+
+ /* Sign */
+ if (signvalue)
+ dopr_outch (buffer, currlen, maxlen, signvalue);
+
+ /* Zeros */
+ if (zpadlen > 0)
+ {
+ while (zpadlen > 0)
+ {
+ dopr_outch (buffer, currlen, maxlen, '0');
+ --zpadlen;
+ }
+ }
+
+ /* Digits */
+ while (place > 0)
+ dopr_outch (buffer, currlen, maxlen, convert[--place]);
+
+ /* Left Justified spaces */
+ while (spadlen < 0) {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ ++spadlen;
+ }
+}
+
+static long double abs_val (long double value)
+{
+ long double result = value;
+
+ if (value < 0)
+ result = -value;
+
+ return result;
+}
+
+static long double pow10 (int exp)
+{
+ long double result = 1;
+
+ while (exp)
+ {
+ result *= 10;
+ exp--;
+ }
+
+ return result;
+}
+
+static long round (long double value)
+{
+ long intpart;
+
+ intpart = value;
+ value = value - intpart;
+ if (value >= 0.5)
+ intpart++;
+
+ return intpart;
+}
+
+static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
+ long double fvalue, int min, int max, int flags)
+{
+ int signvalue = 0;
+ long double ufvalue;
+ char iconvert[20];
+ char fconvert[20];
+ int iplace = 0;
+ int fplace = 0;
+ int padlen = 0; /* amount to pad */
+ int zpadlen = 0;
+ int caps = 0;
+ long intpart;
+ long fracpart;
+
+ /*
+ * AIX manpage says the default is 0, but Solaris says the default
+ * is 6, and sprintf on AIX defaults to 6
+ */
+ if (max < 0)
+ max = 6;
+
+ ufvalue = abs_val (fvalue);
+
+ if (fvalue < 0)
+ signvalue = '-';
+ else
+ if (flags & DP_F_PLUS) /* Do a sign (+/i) */
+ signvalue = '+';
+ else
+ if (flags & DP_F_SPACE)
+ signvalue = ' ';
+
+#if 0
+ if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
+#endif
+
+ intpart = ufvalue;
+
+ /*
+ * Sorry, we only support 9 digits past the decimal because of our
+ * conversion method
+ */
+ if (max > 9)
+ max = 9;
+
+ /* We "cheat" by converting the fractional part to integer by
+ * multiplying by a factor of 10
+ */
+ fracpart = round ((pow10 (max)) * (ufvalue - intpart));
+
+ if (fracpart >= pow10 (max))
+ {
+ intpart++;
+ fracpart -= pow10 (max);
+ }
+
+#ifdef DEBUG_SNPRINTF
+ dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart));
+#endif
+
+ /* Convert integer part */
+ do {
+ iconvert[iplace++] =
+ (caps? "0123456789ABCDEF":"0123456789abcdef")[intpart % 10];
+ intpart = (intpart / 10);
+ } while(intpart && (iplace < 20));
+ if (iplace == 20) iplace--;
+ iconvert[iplace] = 0;
+
+ /* Convert fractional part */
+ do {
+ fconvert[fplace++] =
+ (caps? "0123456789ABCDEF":"0123456789abcdef")[fracpart % 10];
+ fracpart = (fracpart / 10);
+ } while(fracpart && (fplace < 20));
+ if (fplace == 20) fplace--;
+ fconvert[fplace] = 0;
+
+ /* -1 for decimal point, another -1 if we are printing a sign */
+ padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
+ zpadlen = max - fplace;
+ if (zpadlen < 0)
+ zpadlen = 0;
+ if (padlen < 0)
+ padlen = 0;
+ if (flags & DP_F_MINUS)
+ padlen = -padlen; /* Left Justifty */
+
+ if ((flags & DP_F_ZERO) && (padlen > 0))
+ {
+ if (signvalue)
+ {
+ dopr_outch (buffer, currlen, maxlen, signvalue);
+ --padlen;
+ signvalue = 0;
+ }
+ while (padlen > 0)
+ {
+ dopr_outch (buffer, currlen, maxlen, '0');
+ --padlen;
+ }
+ }
+ while (padlen > 0)
+ {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ --padlen;
+ }
+ if (signvalue)
+ dopr_outch (buffer, currlen, maxlen, signvalue);
+
+ while (iplace > 0)
+ dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
+
+ /*
+ * Decimal point. This should probably use locale to find the correct
+ * char to print out.
+ */
+ dopr_outch (buffer, currlen, maxlen, '.');
+
+ while (fplace > 0)
+ dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
+
+ while (zpadlen > 0)
+ {
+ dopr_outch (buffer, currlen, maxlen, '0');
+ --zpadlen;
+ }
+
+ while (padlen < 0)
+ {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ ++padlen;
+ }
+}
+
+static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c)
+{
+ if (*currlen < maxlen)
+ buffer[(*currlen)++] = c;
+}
+#endif /* !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) */
+
+#ifndef HAVE_VSNPRINTF
+int vsnprintf (char *str, size_t count, const char *fmt, va_list args)
+{
+ str[0] = 0;
+ dopr(str, count, fmt, args);
+ return(strlen(str));
+}
+#endif /* !HAVE_VSNPRINTF */
+
+#ifndef HAVE_SNPRINTF
+/* VARARGS3 */
+#ifdef HAVE_STDARGS
+int snprintf (char *str,size_t count,const char *fmt,...)
+#else
+int snprintf (va_alist) va_dcl
+#endif
+{
+#ifndef HAVE_STDARGS
+ char *str;
+ size_t count;
+ char *fmt;
+#endif
+ VA_LOCAL_DECL;
+
+ VA_START (fmt);
+ VA_SHIFT (str, char *);
+ VA_SHIFT (count, size_t );
+ VA_SHIFT (fmt, char *);
+ (void) vsnprintf(str, count, fmt, ap);
+ VA_END;
+ return(strlen(str));
+}
+
+#ifdef TEST_SNPRINTF
+#ifndef LONG_STRING
+#define LONG_STRING 1024
+#endif
+int main (void)
+{
+ char buf1[LONG_STRING];
+ char buf2[LONG_STRING];
+ char *fp_fmt[] = {
+ "%-1.5f",
+ "%1.5f",
+ "%123.9f",
+ "%10.5f",
+ "% 10.5f",
+ "%+22.9f",
+ "%+4.9f",
+ "%01.3f",
+ "%4f",
+ "%3.1f",
+ "%3.2f",
+ NULL
+ };
+ double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996,
+ 0.9996, 1.996, 4.136, 0};
+ char *int_fmt[] = {
+ "%-1.5d",
+ "%1.5d",
+ "%123.9d",
+ "%5.5d",
+ "%10.5d",
+ "% 10.5d",
+ "%+22.33d",
+ "%01.3d",
+ "%4d",
+ NULL
+ };
+ long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
+ int x, y;
+ int fail = 0;
+ int num = 0;
+
+ printf ("Testing snprintf format codes against system sprintf...\n");
+
+ for (x = 0; fp_fmt[x] != NULL ; x++)
+ for (y = 0; fp_nums[y] != 0 ; y++)
+ {
+ snprintf (buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]);
+ sprintf (buf2, fp_fmt[x], fp_nums[y]);
+ if (strcmp (buf1, buf2))
+ {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n",
+ fp_fmt[x], buf1, buf2);
+ fail++;
+ }
+ num++;
+ }
+
+ for (x = 0; int_fmt[x] != NULL ; x++)
+ for (y = 0; int_nums[y] != 0 ; y++)
+ {
+ snprintf (buf1, sizeof (buf1), int_fmt[x], int_nums[y]);
+ sprintf (buf2, int_fmt[x], int_nums[y]);
+ if (strcmp (buf1, buf2))
+ {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n",
+ int_fmt[x], buf1, buf2);
+ fail++;
+ }
+ num++;
+ }
+ printf ("%d tests failed out of %d.\n", fail, num);
+}
+#endif /* SNPRINTF_TEST */
+
+#endif /* !HAVE_SNPRINTF */
diff --git a/strcasecmp.c b/strcasecmp.c
new file mode 100644
index 0000000..e27e46a
--- /dev/null
+++ b/strcasecmp.c
@@ -0,0 +1,47 @@
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#undef HAVE_STRCASECMP
+#undef _GNU_SOURCE
+#undef _ISOC9X_SOURCE
+#undef _OSF_SOURCE
+#undef _XOPEN_SOURCE
+#undef __EXTENSIONS__
+#include <missing.h>
+#include <ctype.h>
+
+int
+strcasecmp (const char *a, const char *b)
+{
+ register int n;
+
+ while (*a == *b || (n = tolower (*a) - tolower (*b)) == 0)
+ {
+ if (*a == '\0')
+ return 0;
+ a++, b++;
+ }
+ return n;
+}
diff --git a/strdup.c b/strdup.c
new file mode 100644
index 0000000..218db8a
--- /dev/null
+++ b/strdup.c
@@ -0,0 +1,45 @@
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#undef HAVE_STRDUP
+#undef _GNU_SOURCE
+#undef _ISOC9X_SOURCE
+#undef _OSF_SOURCE
+#undef _XOPEN_SOURCE
+#undef __EXTENSIONS__
+#include <string.h>
+#include <stdlib.h>
+#include <missing.h>
+
+char *
+strdup (const char *s1)
+{
+ char *p;
+
+ p = malloc (strlen (s1) + 1);
+ if (p != NULL)
+ strcpy (p, s1);
+ return p;
+}
diff --git a/strncasecmp.c b/strncasecmp.c
new file mode 100644
index 0000000..d35d27a
--- /dev/null
+++ b/strncasecmp.c
@@ -0,0 +1,49 @@
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#undef HAVE_STRNCASECMP
+#undef _GNU_SOURCE
+#undef _ISOC9X_SOURCE
+#undef _OSF_SOURCE
+#undef _XOPEN_SOURCE
+#undef __EXTENSIONS__
+#include <missing.h>
+#include <ctype.h>
+
+int
+strncasecmp (const char *a, const char *b, size_t len)
+{
+ register int n;
+
+ if (len < 1)
+ return 0;
+ while (*a == *b || (n = tolower (*a) - tolower (*b)) == 0)
+ {
+ if (*a == '\0' || --len < 1)
+ return 0;
+ a++, b++;
+ }
+ return n;
+}
diff --git a/tokens.c b/tokens.c
new file mode 100644
index 0000000..6926bc7
--- /dev/null
+++ b/tokens.c
@@ -0,0 +1,128 @@
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "tokens.h"
+
+#define ATOMEXCLUDE "\"()<>[]@,;:\\."
+#define XTEXTEXCLUDE " +="
+#define WHITESPACE " \t\r\n\v"
+
+#define SPACE 1
+#define GRAPH 2
+#define ATOM 4
+#define XTEXT 8
+
+static char atomchars[256];
+#define is_atom(c) (atomchars[(int) (c)] & ATOM)
+#define is_xtext(c) (atomchars[(int) (c)] & XTEXT)
+#define is_space(c) (atomchars[(int) (c)] & SPACE)
+
+#define initatom() do { if (!is_space (' ')) _initatom (); } while (0)
+
+static void
+_initatom (void)
+{
+ const char *p;
+ int i;
+
+ /* Mark characters in the printable ASCII range */
+ for (i = ' ' + 1; i <= '~'; i++)
+ atomchars[i] |= (ATOM | GRAPH | XTEXT);
+
+ /* Turn off the ATOM flag on characters that are excluded */
+ for (p = ATOMEXCLUDE; *p != '\0'; p++)
+ atomchars[(int) *p] &= ~ATOM;
+
+ /* Turn off the XTEXT flag on printable characters that should be encoded */
+ for (p = XTEXTEXCLUDE; *p != '\0'; p++)
+ atomchars[(int) *p] &= ~XTEXT;
+
+ /* Mark space characters */
+ for (p = WHITESPACE; *p != '\0'; p++)
+ atomchars[(int) *p] |= SPACE;
+}
+
+const char *
+skipblank (const char *s)
+{
+ initatom ();
+ while (is_space (*s))
+ s++;
+ return s;
+}
+
+int
+read_atom (const char *s, const char **es, char buf[], int len)
+{
+ char *t;
+
+ initatom ();
+ if (!is_atom (*s))
+ return 0;
+ t = buf;
+ do
+ {
+ if (t < buf + len - 1)
+ *t++ = *s;
+ s++;
+ }
+ while (is_atom (*s));
+ *t = '\0';
+ if (es != NULL)
+ *es = s;
+ return t - buf;
+}
+
+static char xdigits[] = "0123456789ABCDEF";
+
+/* Return a pointer to an xtext encoded string.
+ */
+char *
+encode_xtext (char buf[], int len, const char *string)
+{
+ const unsigned char *s;
+ char *t;
+
+ for (s = (const unsigned char *) string, t = buf; *s != '\0'; s++, t++)
+ {
+ if (t - buf > len - 1)
+ return NULL;
+ if (is_xtext (*s))
+ *t = *s;
+ else
+ {
+ *t++ = '+';
+ *t++ = xdigits[(*s & 0xF0) >> 4];
+ *t = xdigits[*s & 0x0F];
+ }
+ }
+ *t = '\0';
+ return buf;
+}
+
diff --git a/tokens.h b/tokens.h
new file mode 100644
index 0000000..3c113f7
--- /dev/null
+++ b/tokens.h
@@ -0,0 +1,29 @@
+#ifndef _tokens_h
+#define _tokens_h
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+int read_atom (const char *s, const char **es, char buf[], int len);
+const char *skipblank (const char *s);
+char *encode_xtext (char buf[], int len, const char *string);
+
+#endif