diff options
author | Pawel Salek <pawsa@damage.localdomain> | 2010-03-04 22:21:56 +0100 |
---|---|---|
committer | Pawel Salek <pawsa@damage.localdomain> | 2010-03-04 22:21:56 +0100 |
commit | af79dfa2d055053c1027e20cf08f0549dc2e9ada (patch) | |
tree | c1fbf971ebdf7913e0325313b701e7f2d2fa9c17 | |
download | libesmtp-af79dfa2d055053c1027e20cf08f0549dc2e9ada.tar.gz libesmtp-af79dfa2d055053c1027e20cf08f0549dc2e9ada.tar.xz libesmtp-af79dfa2d055053c1027e20cf08f0549dc2e9ada.zip |
Initial Commit
-rw-r--r-- | AUTHORS | 1 | ||||
-rw-r--r-- | COPYING | 340 | ||||
-rw-r--r-- | COPYING.LIB | 515 | ||||
-rw-r--r-- | ChangeLog | 1262 | ||||
-rw-r--r-- | INSTALL | 236 | ||||
-rw-r--r-- | Makefile.am | 30 | ||||
-rw-r--r-- | Makefile.in | 828 | ||||
-rw-r--r-- | NEWS | 229 | ||||
-rw-r--r-- | Notes | 95 | ||||
-rw-r--r-- | README | 106 | ||||
-rw-r--r-- | TODO | 31 | ||||
-rw-r--r-- | api.h | 35 | ||||
-rw-r--r-- | auth-client.c | 471 | ||||
-rw-r--r-- | auth-client.h | 91 | ||||
-rw-r--r-- | auth-plugin.h | 56 | ||||
-rw-r--r-- | base64.c | 167 | ||||
-rw-r--r-- | base64.h | 28 | ||||
-rw-r--r-- | concatenate.c | 193 | ||||
-rw-r--r-- | concatenate.h | 42 | ||||
-rw-r--r-- | configure.in | 618 | ||||
-rw-r--r-- | crammd5/Makefile.am | 14 | ||||
-rw-r--r-- | crammd5/Makefile.in | 479 | ||||
-rw-r--r-- | crammd5/client-crammd5.c | 142 | ||||
-rw-r--r-- | crammd5/hmacmd5.c | 120 | ||||
-rw-r--r-- | crammd5/hmacmd5.h | 51 | ||||
-rw-r--r-- | crammd5/md5.c | 297 | ||||
-rw-r--r-- | crammd5/md5.h | 51 | ||||
-rw-r--r-- | doc/api.xml | 2360 | ||||
-rw-r--r-- | errors.c | 296 | ||||
-rw-r--r-- | examples/Makefile | 15 | ||||
-rw-r--r-- | examples/mail-file.c | 567 | ||||
-rw-r--r-- | examples/test-mail | 15 | ||||
-rw-r--r-- | getaddrinfo.c | 314 | ||||
-rw-r--r-- | getaddrinfo.h | 67 | ||||
-rw-r--r-- | gethostbyname.c | 228 | ||||
-rw-r--r-- | gethostbyname.h | 103 | ||||
-rw-r--r-- | headers.c | 906 | ||||
-rw-r--r-- | headers.h | 32 | ||||
-rw-r--r-- | htable.c | 228 | ||||
-rw-r--r-- | htable.h | 39 | ||||
-rw-r--r-- | libesmtp-config.in | 87 | ||||
-rw-r--r-- | libesmtp-private.h | 256 | ||||
-rw-r--r-- | libesmtp.h | 317 | ||||
-rw-r--r-- | libesmtp.spec.in | 82 | ||||
-rw-r--r-- | login/Makefile.am | 11 | ||||
-rw-r--r-- | login/Makefile.in | 476 | ||||
-rw-r--r-- | login/client-login.c | 135 | ||||
-rw-r--r-- | memrchr.c | 43 | ||||
-rw-r--r-- | message-callbacks.c | 92 | ||||
-rw-r--r-- | message-source.c | 218 | ||||
-rw-r--r-- | message-source.h | 42 | ||||
-rw-r--r-- | missing.h | 57 | ||||
-rw-r--r-- | ntlm/Makefile.am | 11 | ||||
-rw-r--r-- | ntlm/Makefile.in | 478 | ||||
-rw-r--r-- | ntlm/client-ntlm.c | 149 | ||||
-rw-r--r-- | ntlm/ntlm.h | 52 | ||||
-rw-r--r-- | ntlm/ntlmdes.c | 157 | ||||
-rw-r--r-- | ntlm/ntlmstruct.c | 331 | ||||
-rw-r--r-- | plain/Makefile.am | 11 | ||||
-rw-r--r-- | plain/Makefile.in | 476 | ||||
-rw-r--r-- | plain/client-plain.c | 113 | ||||
-rw-r--r-- | protocol-states.h | 51 | ||||
-rw-r--r-- | protocol.c | 1570 | ||||
-rw-r--r-- | protocol.h | 43 | ||||
-rw-r--r-- | rfc2822date.c | 118 | ||||
-rw-r--r-- | rfc2822date.h | 27 | ||||
-rw-r--r-- | siobuf.c | 647 | ||||
-rw-r--r-- | siobuf.h | 59 | ||||
-rw-r--r-- | smtp-api.c | 637 | ||||
-rw-r--r-- | smtp-auth.c | 306 | ||||
-rw-r--r-- | smtp-bdat.c | 314 | ||||
-rw-r--r-- | smtp-etrn.c | 251 | ||||
-rw-r--r-- | smtp-tls.c | 715 | ||||
-rw-r--r-- | snprintf.c | 797 | ||||
-rw-r--r-- | strcasecmp.c | 47 | ||||
-rw-r--r-- | strdup.c | 45 | ||||
-rw-r--r-- | strncasecmp.c | 49 | ||||
-rw-r--r-- | tokens.c | 128 | ||||
-rw-r--r-- | tokens.h | 29 |
79 files changed, 21125 insertions, 0 deletions
@@ -0,0 +1 @@ +Brian Stafford <brian@stafford.uklinux.net> @@ -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 + @@ -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: @@ -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. + @@ -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. + @@ -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. + + @@ -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. + @@ -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 <signal.h> + +void +ignore_sigpipe (void) +{ + struct sigaction sa; + + sa.sa_handler = SIG_IGN; + sigemptyset (&sa.sa_mask); + sa.sa_flags = 0; + sigaction (SIGPIPE, &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 "orthogonal" 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 <auth-client.h> +#include <libesmtp.h> + </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 <openssl/ssl.h> +#include <libesmtp.h> + </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 |