pax_global_header00006660000000000000000000000064115556337120014522gustar00rootroot0000000000000052 comment=becdd3e2c1fd61950b6a06e88ba4c99a7858c7fe heirloom-mailx-12.5/000077500000000000000000000000001155563371200143775ustar00rootroot00000000000000heirloom-mailx-12.5/AUTHORS000066400000000000000000000015661155563371200154570ustar00rootroot00000000000000Berkeley Mail was (according to def.h) developed by Kurt Shoens, dated March 25, 1978. I very much regret that it seems impossible to include the people that contributed within the around fifteen years of history of BSD Mail. If you know more about this, contact me. After the 4.4BSD release in 1993, Mail was not further developed officially. The code that Heirloom mailx is based on contains numerous patches from OpenBSD, NetBSD, RedHat and Debian. Namely the NetBSD developer Christos Zoulas wrote much of it. The maintainer and primary developer of Heirloom mailx is Gunnar Ritter. Its development started under the name "nail" in February 2000 and added especially the MIME code, network protocol support, and POSIX conformance improvements. In March 2006, the program has been integrated into the Heirloom project. See the ChangeLog for people who have contributed to mailx. heirloom-mailx-12.5/COPYING000066400000000000000000001003041155563371200154300ustar00rootroot00000000000000/* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * Copyright (c) 1996 * Christos Zoulas. All rights reserved. * Copyright (c) 2000 * Gunnar Ritter. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgements: * This product includes software developed by the University of * California, Berkeley and its contributors. * This product includes software developed by Christos Zoulas. * This product includes software developed by Gunnar Ritter * and his contributors. * 4. Neither the name of the University nor the names of its contributors * nor the name of Gunnar Ritter nor the names of his contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'', AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE DEVELOPERS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ ========================================================================== /* * For base64.c: * * Portions Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore) * * Permission to use, copy, modify, and distribute this material * for any purpose and without fee is hereby granted, provided * that the above copyright notice and this permission notice * appear in all copies, and that the name of Bellcore not be * used in advertising or publicity pertaining to this * material without the specific, prior written permission * of an authorized representative of Bellcore. BELLCORE * MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY * OF THIS MATERIAL FOR ANY PURPOSE. IT IS PROVIDED "AS IS", * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. */ ========================================================================== The Institute of Electrical and Electronics Engineers and The Open Group, have given us permission to reprint portions of their documentation. In the following statement, the phrase ``this text'' refers to portions of the system documentation. Portions of this text are reprinted and reproduced in electronic form in the 'nail' mail user agent, from IEEE Std 1003.1, 2003 Edition, Standard for Information Technology -- Portable Operating System Interface (POSIX), The Open Group Base Specifications Issue 6, Copyright (C) 2001-2003 by the Institute of Electrical and Electronics Engineers, Inc and The Open Group. In the event of any discrepancy between these versions and the original IEEE and The Open Group Standard, the original IEEE and The Open Group Standard is the referee document. The original Standard can be obtained online at http://www.opengroup.org/unix/online.html . This notice shall appear on any product containing this material. ========================================================================== /* * imap_gssapi.c is partially derived from sample code in: * GSS-API Programming Guide * Part No: 816-1331-11 * Sun Microsystems, Inc. 4150 Network Circle Santa Clara, CA 95054 U.S.A. * * (c) 2002 Sun Microsystems */ /* * Copyright 1994 by OpenVision Technologies, Inc. * * Permission to use, copy, modify, distribute, and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appears in all copies and * that both that copyright notice and this permission notice appear in * supporting documentation, and that the name of OpenVision not be used * in advertising or publicity pertaining to distribution of the software * without specific, written prior permission. OpenVision makes no * representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied warranty. * * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ ========================================================================== /* md5.h and md5.c are derived from RFC 1321: Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights reserved. License to copy and use this software is granted provided that it is identified as the "RSA Data Security, Inc. MD5 Message-Digest Algorithm" in all material mentioning or referencing this software or this function. License is also granted to make and use derivative works provided that such works are identified as "derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm" in all material mentioning or referencing the derived work. RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided "as is" without express or implied warranty of any kind. These notices must be retained in any copies of any part of this documentation and/or software. ========================================================================== /* * hmac.c is derived from: Network Working Group H. Krawczyk Request for Comments: 2104 IBM Category: Informational M. Bellare UCSD R. Canetti IBM February 1997 HMAC: Keyed-Hashing for Message Authentication Status of This Memo This memo provides information for the Internet community. This memo does not specify an Internet standard of any kind. Distribution of this memo is unlimited. Appendix -- Sample Code ========================================================================== Parts of nss.c are derived from the Mozilla NSS 3.9.2 source, mozilla/security/nss/cmd/smimetools/cmsutil.c. Therefore: MOZILLA PUBLIC LICENSE Version 1.1 --------------- 1. Definitions. 1.0.1. "Commercial Use" means distribution or otherwise making the Covered Code available to a third party. 1.1. "Contributor" means each entity that creates or contributes to the creation of Modifications. 1.2. "Contributor Version" means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor. 1.3. "Covered Code" means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof. 1.4. "Electronic Distribution Mechanism" means a mechanism generally accepted in the software development community for the electronic transfer of data. 1.5. "Executable" means Covered Code in any form other than Source Code. 1.6. "Initial Developer" means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A. 1.7. "Larger Work" means a work which combines Covered Code or portions thereof with code not governed by the terms of this License. 1.8. "License" means this document. 1.8.1. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein. 1.9. "Modifications" means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is: A. Any addition to or deletion from the contents of a file containing Original Code or previous Modifications. B. Any new file that contains any part of the Original Code or previous Modifications. 1.10. "Original Code" means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code, and which, at the time of its release under this License is not already Covered Code governed by this License. 1.10.1. "Patent Claims" means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor. 1.11. "Source Code" means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor's choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge. 1.12. "You" (or "Your") means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, "You" includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. 2. Source Code License. 2.1. The Initial Developer Grant. The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims: (a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, and/or as part of a Larger Work; and (b) under Patents Claims infringed by the making, using or selling of Original Code, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Code (or portions thereof). (c) the licenses granted in this Section 2.1(a) and (b) are effective on the date Initial Developer first distributes Original Code under the terms of this License. (d) Notwithstanding Section 2.1(b) above, no patent license is granted: 1) for code that You delete from the Original Code; 2) separate from the Original Code; or 3) for infringements caused by: i) the modification of the Original Code or ii) the combination of the Original Code with other software or devices. 2.2. Contributor Grant. Subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license (a) under intellectual property rights (other than patent or trademark) Licensable by Contributor, to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Code and/or as part of a Larger Work; and (b) under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: 1) Modifications made by that Contributor (or portions thereof); and 2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination). (c) the licenses granted in Sections 2.2(a) and 2.2(b) are effective on the date Contributor first makes Commercial Use of the Covered Code. (d) Notwithstanding Section 2.2(b) above, no patent license is granted: 1) for any code that Contributor has deleted from the Contributor Version; 2) separate from the Contributor Version; 3) for infringements caused by: i) third party modifications of Contributor Version or ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or 4) under Patent Claims infringed by Covered Code in the absence of Modifications made by that Contributor. 3. Distribution Obligations. 3.1. Application of License. The Modifications which You create or to which You contribute are governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Covered Code may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients' rights hereunder. However, You may include an additional document offering the additional rights described in Section 3.5. 3.2. Availability of Source Code. Any Modification which You create or to which You contribute must be made available in Source Code form under the terms of this License either on the same media as an Executable version or via an accepted Electronic Distribution Mechanism to anyone to whom you made an Executable version available; and if made available via Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to such recipients. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party. 3.3. Description of Modifications. You must cause all Covered Code to which You contribute to contain a file documenting the changes You made to create that Covered Code and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Code provided by the Initial Developer and including the name of the Initial Developer in (a) the Source Code, and (b) in any notice in an Executable version or related documentation in which You describe the origin or ownership of the Covered Code. 3.4. Intellectual Property Matters (a) Third Party Claims. If Contributor has knowledge that a license under a third party's intellectual property rights is required to exercise the rights granted by such Contributor under Sections 2.1 or 2.2, Contributor must include a text file with the Source Code distribution titled "LEGAL" which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If Contributor obtains such knowledge after the Modification is made available as described in Section 3.2, Contributor shall promptly modify the LEGAL file in all copies Contributor makes available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Covered Code that new knowledge has been obtained. (b) Contributor APIs. If Contributor's Modifications include an application programming interface and Contributor has knowledge of patent licenses which are reasonably necessary to implement that API, Contributor must also include this information in the LEGAL file. (c) Representations. Contributor represents that, except as disclosed pursuant to Section 3.4(a) above, Contributor believes that Contributor's Modifications are Contributor's original creation(s) and/or Contributor has sufficient rights to grant the rights conveyed by this License. 3.5. Required Notices. You must duplicate the notice in Exhibit A in each file of the Source Code. If it is not possible to put such notice in a particular Source Code file due to its structure, then You must include such notice in a location (such as a relevant directory) where a user would be likely to look for such a notice. If You created one or more Modification(s) You may add your name as a Contributor to the notice described in Exhibit A. You must also duplicate this License in any documentation for the Source Code where You describe recipients' rights or ownership rights relating to Covered Code. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Code. However, You may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear than any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer. 3.6. Distribution of Executable Versions. You may distribute Covered Code in Executable form only if the requirements of Section 3.1-3.5 have been met for that Covered Code, and if You include a notice stating that the Source Code version of the Covered Code is available under the terms of this License, including a description of how and where You have fulfilled the obligations of Section 3.2. The notice must be conspicuously included in any notice in an Executable version, related documentation or collateral in which You describe recipients' rights relating to the Covered Code. You may distribute the Executable version of Covered Code or ownership rights under a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable version does not attempt to limit or alter the recipient's rights in the Source Code version from the rights set forth in this License. If You distribute the Executable version under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or any Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer. 3.7. Larger Works. You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Code. 4. Inability to Comply Due to Statute or Regulation. If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Code due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. 5. Application of this License. This License applies to code to which the Initial Developer has attached the notice in Exhibit A and to related Covered Code. 6. Versions of the License. 6.1. New Versions. Netscape Communications Corporation ("Netscape") may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number. 6.2. Effect of New Versions. Once Covered Code has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Covered Code under the terms of any subsequent version of the License published by Netscape. No one other than Netscape has the right to modify the terms applicable to Covered Code created under this License. 6.3. Derivative Works. If You create or use a modified version of this License (which you may only do in order to apply it to code which is not already Covered Code governed by this License), You must (a) rename Your license so that the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", "MPL", "NPL" or any confusingly similar phrase do not appear in your license (except to note that your license differs from this License) and (b) otherwise make it clear that Your version of the license contains terms which differ from the Mozilla Public License and Netscape Public License. (Filling in the name of the Initial Developer, Original Code or Contributor in the notice described in Exhibit A shall not of themselves be deemed to be modifications of this License.) 7. DISCLAIMER OF WARRANTY. COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. 8. TERMINATION. 8.1. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Covered Code which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive. 8.2. If You initiate litigation by asserting a patent infringement claim (excluding declatory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You file such action is referred to as "Participant") alleging that: (a) such Participant's Contributor Version directly or indirectly infringes any patent, then any and all rights granted by such Participant to You under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively, unless if within 60 days after receipt of notice You either: (i) agree in writing to pay Participant a mutually agreeable reasonable royalty for Your past and future use of Modifications made by such Participant, or (ii) withdraw Your litigation claim with respect to the Contributor Version against such Participant. If within 60 days of notice, a reasonable royalty and payment arrangement are not mutually agreed upon in writing by the parties or the litigation claim is not withdrawn, the rights granted by Participant to You under Sections 2.1 and/or 2.2 automatically terminate at the expiration of the 60 day notice period specified above. (b) any software, hardware, or device, other than such Participant's Contributor Version, directly or indirectly infringes any patent, then any rights granted to You by such Participant under Sections 2.1(b) and 2.2(b) are revoked effective as of the date You first made, used, sold, distributed, or had made, Modifications made by that Participant. 8.3. If You assert a patent infringement claim against Participant alleging that such Participant's Contributor Version directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by such Participant under Sections 2.1 or 2.2 shall be taken into account in determining the amount or value of any payment or license. 8.4. In the event of termination under Sections 8.1 or 8.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or any distributor hereunder prior to termination shall survive termination. 9. LIMITATION OF LIABILITY. UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. 10. U.S. GOVERNMENT END USERS. The Covered Code is a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer software" and "commercial computer software documentation," as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Code with only those rights set forth herein. 11. MISCELLANEOUS. This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by California law provisions (except to the extent applicable law, if any, provides otherwise), excluding its conflict-of-law provisions. With respect to disputes in which at least one party is a citizen of, or an entity chartered or registered to do business in the United States of America, any litigation relating to this License shall be subject to the jurisdiction of the Federal Courts of the Northern District of California, with venue lying in Santa Clara County, California, with the losing party responsible for costs, including without limitation, court costs and reasonable attorneys' fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License. 12. RESPONSIBILITY FOR CLAIMS. As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability. 13. MULTIPLE-LICENSED CODE. Initial Developer may designate portions of the Covered Code as "Multiple-Licensed". "Multiple-Licensed" means that the Initial Developer permits you to utilize portions of the Covered Code under Your choice of the NPL or the alternative licenses, if any, specified by the Initial Developer in the file described in Exhibit A. EXHIBIT A -Mozilla Public License. ``The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is ______________________________________. The Initial Developer of the Original Code is ________________________. Portions created by ______________________ are Copyright (C) ______ _______________________. All Rights Reserved. Contributor(s): ______________________________________. Alternatively, the contents of this file may be used under the terms of the _____ license (the "[___] License"), in which case the provisions of [______] License are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of the [____] License and not to allow others to use your version of this file under the MPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the [___] License. If you do not delete the provisions above, a recipient may use your version of this file under either the MPL or the [___] License." [NOTE: The text of this Exhibit A may differ slightly from the text of the notices in the Source Code files of the Original Code. You should use the text of this Exhibit A rather than the text found in the Original Code Source Code for Your Modifications.] heirloom-mailx-12.5/ChangeLog000066400000000000000000002237431155563371200161640ustar00rootroot00000000000000[12.5] as of 6/20/10 * Fixed a message corruption that occurred when the "inc" command was used with a mbox format mailbox after encrypted messages had been viewed (reported by Martin Neitzel). * Fixed a condition that caused mailx to hang when looking at a message, copying that message, and issuing a "z" command evaluating an uncached portion of an IMAP folder. * Make it compile with OpenSSL 1.0.0-beta2 (patch by Bernhard Rosenkränzer). * When executing an "account" command, ensure that the values of the "hold", "keepsave", "append", and "emptybox" variables remain those of the previous account for all outstanding operations on that account (bug reported by Jeff Woodall). * Support Gmail IMAP semantics of "delete" turning into "archive". Previously, when mailx was setting the "\Deleted" flag on a message, it did not set any other flags. This caused mails that had been read and then deleted to appear as unread in Gmail's "All Mail". Now, flags besides "\Deleted" are also set, so that they are kept in the archived copy (reported by Jeremy O'Brien). * For RFC 2047 MIME "encoded-word" parts in headers, assume that the end of each word resets the conversion state (Yedidyah Bar-David). * When the ORGANIZATION variable has an empty value, do not generate an "Organization:" header field. Previously, this condition resulted in mailx refusing to send mail (Thomas E. Kammeyer). * When classifying messages with the junk filter, when selecting the most representative tokens, prefer non-junk tokens over junk tokens if both have the same (reversed) probability, in order to avoid false positives. * When calculating the probabilities of tokens that have occurred only in good messages or only in junk messages so far, and these tokens occurred less than ten times, give them slightly less than the minimum/maximum. * Fixed "unignore" and similar commands; they did not work at all so far (http://www.freebsd.org/cgi/query-pr.cgi?pr=146280). [12.4] released 7/29/08 * With the "-E" command line option or if the "skipemptybody" variable is set, outgoing messages that contain no text in their first or only part are not sent but silently discarded (contributed by Eygene Ryabinkin). * Support for SMTP AUTH PLAIN has been added (contributed by Joe Feise). Set "smtp-auth=plain" to use it. * When the text of an outgoing message contains illegal byte sequences in non-interactive mode, do not refuse sending it but send it with the "application/octet-stream" content type instead (thanks for the report and further help to Hilko Bengen). * When an attachment that would have a "text/something" content type contains illegal byte sequences, it is now reliably sent out with the "application/octet-stream" content type instead (bugreport by Hilko Bengen). * The values of the "sendcharsets" variable are now separately traversed for each part of a multipart message, so that if e.g. the main body can be represented in ISO-8859-1 but an attachment text needs UTF-8, these two encodings will be chosen, respectively. * Fixed a bug that caused messages to be truncated with IMAP servers that use LF as line ending in message data, such as Google Mail (reported by Matthew L. Shobe). * Do not run filename expansion for IMAP or POP3 mailboxes names, making it possible to select mailboxes that contain both brackets and spaces in their names (reported by Matthew L. Shobe). * Fixed the format of the timezone in "Date" header fields for zones in the Western Hemisphere whose offsets are not an integral number of hours (patch by Matthew Fischer). * Print an error message when an attempt is made to S/MIME sign a message but no "from" address can be determined (Georgi Stanojevski). * Made the getopt() substitute work with the getopt$UNIX2003() function redirection mechanism on Mac OS X Leopard. * Fixed an incorrect type of enum (patch by Matthew Fischer). [12.3] released 7/15/07 * Multibyte characters are now handled correctly when printing texts encoded in base64. Previously, if a multibyte character spanned across a line in the base64-encoded representation, it was not recognized, and substitution characters were printed instead. * In an SMTP exchange, ignore if the server closes the connection following the QUIT command instead of sending a proper response code, fixing a problem with GMail (Dr. Gary E. Rafe). * A null pointer dereference that lead to a segmentation fault when the user hit return at a yes/no question has been fixed (patch by Martin Neitzel). * If no proper boundary is present in a MIME multipart message, print a notification message since otherwise no content is shown at all (problem reported by Thomas Wolff). * When both standard input and standard output refer to a terminal, ignore SIGPIPE since there is no reason why it should ever terminate mailx then. This fixes problems with mailx terminating when reading a message and ^C is typed in the pager under some circumstances (analysis by Stephen Isard and Perry Hutchison). * Build without socket support again (patch by Andreas Niederl). * Fixed a single quote in "makeconfig" that could cause a problem with the C preprocessor (patch by Martin Neitzel). * Building with recent versions of Mozilla NSS is now possible. [12.2] released 1/7/07 * When a MIME message part is not of the "text/" content type, always send it in base64 to avoid issues with CRLF conversion (David Ronis). * Character set conversion is now also applied to attachments if they have the "text/" content type. This is necessary because with the "sendcharsets" variable, the character set declaration that becomes effective in the message part is otherwise unpredictable. * Character set conversion is now only applied to message parts that have the "text/" content type. Previously, a message part that did not contain any control characters but was not of type "text/" was converted. * When message file names in a maildir folder do not contain the ":2," preceding the flags part, append one (Brandon Andrews). * An invalid memory access when determining the current message in threaded mode has been fixed (MJ Ray). * A "STRIP" makefile variable has been introduced to control the strip command at "make install". * The "remove" command no longer causes a core dump because of a null pointer dereference when it is given a name with invalid shell syntax. * Made it compile on UnixWare again (patch by Joe Julian). * Fixes to build with more recent versions of Mozilla NSS. [12.1] released 6/15/06 * The base64 encoder also used an invalid encoding for the last unit of data if (bytes_of_input % 3 == 1). As with the previous similar bug, most base64 decoders, including mailx's own, ignore such bits since base64 data is always a multiple of 8-bit bytes. (Patch by Lukasz Stelmach.) * Quotes are now placed around the "protocol" parameter in S/MIME headers in accordance to RFC 2045 (Marco Arioli, Jeffrey Stedfast). * The conversion of messages in state-dependent encodings has been fixed; garbage characters could be inserted because the state was not properly reset when a buffer was enlarged (patch by Matthew Fischer). * A warning about an initalization of an unsigned value to -1 has been eliminated (patch by Matthew Sienkiewicz). * Percent characters in the "prompt" variable are now handled correctly; values of "folder" beginning with an at character and longer than the maximum system path length do not cause a heap overflow anymore (patch by Ulf Harnhammar). [12.0] released 3/4/06 * The program has been integrated into the Heirloom project as "mailx". The new project homepage is at . The name "nail" will persist in some places, and the mailing list will remain with the same address for now. If you want to continue to call the program as "nail", create a symbolic link to "mailx". * Mailx no longer prints a space before the address in "MAIL FROM:" and "RCPT TO:" in SMTP. This should make it work with Sendmail X (bugs reported by Stephane Lentz and Claus Assmann). * The new "-S variable[=value]" command line option sets the internal option "variable", and, in case of a string option, assigns "value" to it (based on a request by Dmitry Butskoy). * If the "smtp-auth-user" variable is set but neither "smtp-auth-password" nor a matching "smtp-auth-password-user@host" can be found, the user is asked for the SMTP AUTH password on the terminal (proposed by Svend Sorensen). * When a TERM signal is sent to the SMTP transfer child process, it will abort and save the message to the "dead.letter" file (proposed by Perry Hutchison). * Use of the "resend" command with multiple messages at once now works as documented (bug reported by Svend Sorensen). * When the "next" or newline command is typed as the first command after a folder has been opened, the first (new) message is printed even if a folder hook has been executed previously (bugreport by Stephen Isard). * In combination with OpenSSL, when a SIGPIPE was received on an SSL socket during a call to SSL_read() e.g. because an IMAP connection had timed out, a segmentation fault occurred because OpenSSL apparently cannot handle calls to SSL_shutdown() from the signal handler. This has been fixed (bug reported by Svend Sorensen). * Building without socket support works again (Patch by Fernando J. Pereda). [11.25] released 7/29/05 * The base64 encoder used an invalid encoding for the last unit of data if (bytes_of_input % 3 == 2); it wrote two encoded bits beyond the last byte then. Most base64 decoders, including nail's own, ignore such bits since base64 data is always a multiple of 8-bit bytes. This might explain why no harm caused by this error has been found for years. (Bug reported by Lukasz Iwaszkiewicz). * When a completely cached IMAP message was copied on a server supporting the UIDPLUS IMAP extension and the message had not been printed during the current session yet, the cached entry for the target message contained the message body twice. This bug has been fixed, but cached messages written by previous nail versions may still suffer from this error. To get rid of them, delete them from the IMAP cache directory. The file README in that directory explains its structure. * When sending messages, input data that lacks a terminating newline is now always encoded in quoted-printable. * Wildcard host names in certificates according to RFC 2595 are now accepted (Bug reported by Svend Sorensen). * The new "maximum-unencoded-line-length" variable allows to configure the limit on the line length that causes an ASCII text message to be sent in quoted-printable if exceeded (on request by Greg Cox). * Since RFC 2595 allows wildcards at other places than RFC 2818 which is implemented by Mozilla NSS, host name verification is now done separately. * The "sort xyz" commands now uncollapse all threads of a previously threaded folder view; messages in collapsed threads were previously not shown when the folder was sorted. * The junk mail filter now also ignores the "X-pstn" header fields generated by "postini" filtering software. [11.24] released 7/14/05 * When the 'replyto' variable was set, sending a message resulted in a segmentation fault due to a NULL pointer dereference (Bug reported by Nick Pasich). * An 'imap-list-depth' variable was introduced to control the maximum depth of the folder traversal for the 'folders' command if the folder separator on the IMAP server is not the slash '/'. * If standard output is not a terminal device, the output of the 'folders' command applied to an IMAP account is no longer arranged into columns. * The new '-R' option causes all folders to be opened read-only. * When the From address was changed with '~H', the value obtained from the 'from' variable was still used for the SMTP transfer, for generating the Message-Id, and for S/MIME signing. This has been fixed; the changed address is now used throughout. * When reading header fields from a terminal, the input buffer could overflow by one byte (Patch by Ulf Harnhammar). * Only applicable if Mozilla NSS is used for SSL support: If the certificate of the server contains a dNSName, NSS ignores the CN. Thus if the CN contains a name that does not also appear as one of the dNSNames, the certificate is rejected. This case is now verified separately in nail, and the certificate is accepted if the CN matches the host name of the server. [11.23] released 6/28/05 * The output for the '~p' tilde escape was extended to include the fields implied by the 'from', 'replyto', 'sender', and 'ORGANIZATION' variables (Proposed by Bob Tennent). * The 'autocc' and 'autobcc' variables are now evaluated before composition of a message begins. The resulting addresses can thus be edited and viewed using the '~b', '~c', '~h', and '~p' tilde escapes, but changing the values of the 'autocc' and 'autobcc' variables using '~:set' has no effect on the currently composed message anymore. * The values of the 'from' and 'replyto' variables can now contain multiple addresses. * A 'sender' variable was introduced to set the content of the 'Sender:' field for outgoing messages. * A '~H' tilde escape was introduced. It allows to set message-specific values for the 'From', 'Reply-To', 'Sender', and 'Organization' header fields. These fields are now also accepted in the editor output with the 'editheaders' variable. * The '-r' option now actually disables tilde escapes as documented (Bugreport by Bob Tennent). * The INSTALL file now notes that sendmail must support the '-r' option if the '-r' option of nail is used (Issue reported by Attila Zimler and Allan Peda). * The 'move' command does not anymore mark messages as moved if writing to an IMAP folder fails. * The 'undelete' command now also clears the 'saved' flags of messages that have been moved. * Fixed a crash due to a null pointer dereference that occurred when both control and non-ASCII bytes but no NUL bytes were present in a message body and no content type was defined in a mime.types file. * The ~F and ~M tilde escapes did not work for interpolating messages with binary attachments anymore since version 11.13 (Bugreport by Tivon Luker). * The current message is now shown in the header summary even if it has been killed. * The 'kill' command did not operate correctly on 64-bit big-endian machines. * A notification is now printed when text is saved to dead.letter. * Changing %{prefix} in nail.spec works now for the RPM %install and %files step too (Patch by Allan Peda). [11.22] released 3/20/05 * In threaded mode, the thread structure is now shown using line-drawing characters in the header summary. (This looks best on a UTF-8 terminal.) * If a message contains both a 'References' and an 'In-Reply-To' header field, 'In-Reply-To' is now preferred for threading and the ',' address. * When a message part of content-type text/anything is written with the pipe-type/subtype mechanism, it is now converted to the character encoding of the locale (as it was before 11.14 or so). * When using a hostname with a port number attached with SMTP STARTTLS, strip the port number for correct host name verification (Bugreport by Gary E. Rafe). [11.21] released 2/19/05 * When mail is sent using SMTP, 'Bcc:' fields are not passed to the server anymore (Bugreport by Matthew Fischer.) * When replying to a message part for which a 'pipe-type/subtype' variable was set, quotes were inserted in the text sent to the pipe instead of in the output received from it (since v. 11.14 or so). * A message that is already marked as answered is not marked again if it is replied to and the 'markanswered' variable is set. * Nail no longer generates an error if renaming a message in a maildir folder fails because both the old and the new link have the same name (i.e. if the rename operation was superfluous). * The new 'sendcharsets' variable was incorrectly named 'charsets' in nail.rc in the last version. If nail 11.20 was the first version of nail you ever installed, you should correct this by hand since the file is not overwritten by 'make install'. (Bugreport by Matthew Fischer.) * Mozilla NSS error reports are more detailed now. * For the 'touch', 'mbox', 'unread', and 'new' commands, a following 'next' command will advance to the next message as if the current one had been printed, as it has been the case with traditional mailx (Bugreport by Michael Sipser). * Files in maildir folders are now read in the order obtained from readdir(). * When the variables 'sign' or 'Sign' are set to the empty string, they are treated as if they had been unset for the '~a' or '~A' commands. [11.20] released 1/13/05 * A 'forward-as-attachment' variable has been introduced. If it is set, the 'forward' command uses a MIME 'message/rfc822' attachment for the original message instead of including it in the newly created text. * The character set to be used for outgoing messages can now be specified with the 'sendcharsets' variable. It can contain multiple values, with the first appropriate one being used to send the message. The 'charset' variable is still accepted but has been removed from the documentation. * If 'sendcharsets' contains more than one character set name, the '~@' tilde escape now asks about character sets for individual attachments if it is invoked without arguments. * If neither the 'sendcharsets' nor the 'charset' variable is set, the character set for outgoing messages that contain non-ASCII characters is now 'utf-8' instead of 'iso-8859-1'. * When the 'encoding' variable was set to '8bit' and a message contained characters that were not representable in the outgoing character set in its body but not in its header, the characters were replaced by question marks and the message was sent, instead of being rejected (Bugreport by Suresh Ramasubramanian). * When sending messages in 8bit, conversions from a multibyte terminal character set failed because multibyte input characters where split across two conversion buffers if they spanned across buffer boundaries. * A 'reply-in-same-charset' variable has been introduced. If it is set, nail tries to reply in the character set of the original message first. * When the header of an outgoing message contained a non-ASCII character but the body did not, the name of the international character set was used for declaring the content-type of the body too. Us-ascii is now used for the body in such cases. * When the 'fullnames' variable was set and the full name of a recipient was the only string in both header and body of an outgoing message that contained non-ASCII characters, no conversion between terminal character set and outgoing character set was performed for it. * For files that contain ASCII control characters but no NULs, a 'charset' parameter was erroneusly generated even though these are not 'text/plain' according to MIME. * Resolved a symbol conflict with SVR4 systems like Solaris that lead to failed builds because of a redeclaration of 'enum idtype'. * When announcing messages after a folder open or a 'newmail' command, a deleted message can no longer become the current message unless all the messages have been deleted. This applies to message deleted by the 'folder-hook' mechanism in particular. [11.19] released 1/2/05 * A 'chained-junk-tokens' variable was introduced for the Bayesian junk mail filter. If set, the filter considers two-word tokens as described in Jonathan A. Zdziarski's paper 'Advanced Language Classification using Chained Tokens', . * When running in a UTF-8 locale, unprintable characters are now replaced by Unicode control pictures or the Unicode replacement character when printing to a terminal. * The 'show' command now also avoids writing unprintable characters to a terminal. Same for the 'classify' command with the 'verbose' option set. * Only MIME-encode words in header fields if they start with '=?' or end with '?=' (instead of encoding any words containing '?', '=', or '_'), and declare them as 'us-ascii' if they do not contain any bytes with the highest bit set (Bugreport by Suresh Ramasubramanian). * Fixed a junk mail tokenizer bug which caused the remainder of a message to be ignored if a period occurred at the first position following another token boundary. * 'In-Reply-To' fields that refer to multiple message-IDs are now handled; the first matching ID wins for the 'thread' command, and the ',' address selects all referenced messages. [11.18] released 12/9/04 * When the 'write' command asks for an attachment filename and a '|' is given as the first character, the attachment is piped to the rest of the string interpreted as a shell command (contributed by Stephen Isard). * The ~p escape now includes recipients that are local files in its output (Bugreport by Bob Tennent). * The 'ungood' and 'unjunk' commands put the wrong marking on the messages they were applied too (regardless of the database update they performed). 'Ungood' now marks a message as being junk, and 'unjunk' clears the junk mark. * A 'record-resent' variable has been introduced. If it is set, the 'resend' and 'Resend' commands save messages to the 'record' folder. (Proposed by Bob Tennent.) * 'redirect' and 'Redirect' are available as aliases for 'resend' and 'Resend', respectively (proposed by Bob Tennent). * A 'forward' command has been introduced that behaves more like the forwarding mechanisms of other contemporary mail user agents. Along with it, there are 'fwdignore', 'fwdretain', 'unfwdignore', and 'unfwdretain' commands, and a 'fwdheading' variable. The 'forward' command is probably only useful with a 'fwdretain' command executed before. Such a command is executed by default in /etc/nail.rc for new installations; for other sites, it is recommended that it be added manually. * Characters with width 0 (e.g. Unicode combining characters) are now handled correctly in header summaries. * When a message in a multibyte character set like UTF-8 that was encoded as quoted-printable was printed and a multibyte character was separated by a '=' continued line, it was not decoded correctly since release 11.14. [11.17] released 11/18/04 * When the 'write' command was used to save attachments in releases after 11.14, the output file was not properly closed. Since writes to that file are buffered, data was only completely written when nail was quit. [11.16] released 11/18/04 * When a header field line that started and ended with a MIME encoded word followed a line that ended with a MIME encoded word, the newline character preceding the next header field was erroneously omitted when a message was displayed since version 11.14. * If a header fields contains a MIME encoded word and a large string of ASCII characters at another point, the large string is encoded as multiple encoded words to comply to RFC 2047. * The pipe-type/subtype mechanism did not work for MIME contents other than text/anything since release 11.14 (Bugreport by Bob Tennent). * When S/MIME support was built using Mozilla NSS, the verification of an encrypted message that had not yet been downloaded from an IMAP or POP3 server failed at the first attempt. * Previous versions refused to write messages or attachments to /dev/null on some Solaris versions. * If the 'from' variable is set to an address that does not contain an '@' character, no (syntactically incorrect) Message-ID is generated anymore. [11.15] released 11/10/04 * The compilation process was fixed to work without CPPFLAGS=-D_GNU_SOURCE on Linux/glibc again, although mremap() is not available then. It is therefore recommended to use CPPFLAGS=-D_GNU_SOURCE with Linux/glibc. (Bugreports by Stephen Isard, Laurens Blankers, Bob Tennent.) * The handling of interrupts during IMAP and POP3 operations has been revised as discussed on the mailing list. See the NOTES section in the manual page for a description. * When an editor is invoked using ~e, ~v, 'edit', or 'visual', interrupts are no longer ignored in its process. With most editor implementations, this does not make any difference since they enable interrupts themselves again; however, traditional implementations of 'ed' do not. * The 'forward' command has been renamed to 'resend' for alignment with the terminology of RFC 2822; it is still available under its old name for at least a transition period. Also, 'Resent-From:' and related fields are now prepended to older fields as specified. * A 'seen' command has been introduced; it takes a message list and marks the messages as having been read. * The semantics of the 'unjunk' command have been changed; it is now exactly the opposite of the 'junk' command and undoes its effects. An 'ungood' command has also been introduced to do the same for a previous 'good' command. * Since many versions of Linux do not correctly update the modification time of a file with mmap(), utime() is now called to force an update when the junk mail database is changed. * A mbox-style "From " line is now generated when a "message/rfc822" MIME part is printed. * The 'multipart/digest' MIME content type is now properly handled. * When a malformed MIME multipart message is encountered that contains the boundary string on two immediately following lines, the second line is no longer treated as the begin of a new part. * The makeconfig script and the installation instructions now include support for building nail with Mozilla NSS on Solaris. * Building IMAP GSSAPI authentication with Heimdal Kerberos libraries works again. [11.14] released 11/4/04 * A 'Z' command was introduced. It is similar to the 'z' command, but scrolls only to header groups that contain at least one new or flagged message. This is useful for reading large mailing lists. * The attachment decoding functions have been rewritten. As a result, nail is now able to decrypt and display encrypted parts of multipart messages. * For MIME messages of type 'multipart/alternative', only the text/plain part is displayed now if one is present with the 'print'-like commands. The 'print-alternatives' variable can be set to print all parts, as it was done previously. The 'Print'-like commands always print all parts. * The ~F and ~M escapes now cause all parts of MIME multipart messages to be included in the message being composed. * A 'show' command was introduced to make the raw MIME-encoded message text visible. * Modifications to messages made using the 'edit' and 'visual' commands do no longer cause changes to the current folder. This is the behavior demanded by POSIX.1-2004. MIME decoding and decryption are now performed before each message is passed to the editor. If you prefer the old behavior, set the new 'writebackedited' variable. * When large amounts of messages were deleted on an IMAP server (or many other STORE commands were generated e.g. because many messages were marked as read), nail sent all of these commands asynchronously. This did slow down operation with some servers and eventually lead to a deadlock after some thousand commands were sent. Nail now waits for server responses in defined intervals. (Bugreport by Stephen Isard.) * The handling of read-only compressed mbox format folders was fixed. * In version 11.12, fcntl() read locking was erroneously introduced for files other than system mailboxes too. It is now disabled for such files again. * If the junk mail database is stored in uncompressed format, mmap() is now used to access it if possible. * Recipients that are file names are now removed from the Cc: and Bcc: fields of outgoing mail too, as it was already done for To:. * If S/MIME support was built using OpenSSL and an encrypted message was tried to be decrypted without an appropriate private key, nail dumped core due to a NULL pointer dereference. It now fails sanely. [11.13] released 10/27/04 * Backslashes outside a quoted command argument are now handled properly. * For junk mail detection, the characters '_', '#', '&', and '%' are now considered as word constituents. Also the '/' character is kept if it occurs between digits, and the ';' character if a '&' was previously encountered within the same word. * For junk mail detection, fields of MIME part headers are now handled like fields of the main header. * For junk mail detection, most HTML tags are now ignored, similar to the proposal in Paul Graham's 'Better Bayesian Filtering'. Also, the top and bottom probabilities are now adjusted to the number of messages used for building the database. * When the junk mail database is updated, it is now truncated beyond the last byte just written. Previously, stale data could have remained after this point, which was no problem for nail itself, but could make zcat(1) fail on the file. * In the junk mail database, hashes of 56 bits are now used to represent words. A side-effect is that it is possible to test for occurences of given words with a higher certainty. * To take advantage of the changes to the junk mail recognition method, you have to delete your junk mail database and have to create it again. * The new 'probability' command prints the junk statistics for given words. * The 'answered', 'classify, 'draft', 'flag', 'good', 'junk', 'undraft', 'unflag', and 'unkill' command now make their argument the current message. This is now also done by the 'score' command unless the new score of the message is negative. * Very old versions of OpenSSL are now detected by the configuration script. OpenSSL code will not be included if the version is too old (Bugreport by Thomas Voegtle). [11.12] released 10/21/04 * Blank characters between MIME encoded words in header fields are now discarded for display purposes as demanded by RFC 2047. * Unless the 'fullnames' variable was set, nail did not generate 'To' header fields when replying to messages since version 11.5 (Bugreport by Bob Tennent). * An internal problem in the token scanning for junk mail detection has been fixed; the end of the mail header was not always correctly determined. * Special characters in IMAP or POP3 account strings like 'imap://user@host' can now be escaped using URL notation, e.g. 'foo%2Fbar' for 'foo/bar'. It was previously not possible to use these characters. This is a slightly incompatible change as the '%' character must now also be escaped, as '%25'. (Bugreport by Thomas Wolff.) * The system mailbox is now locked using fcntl(2) for reading too (Problem reported by Thomas Wolff). * When new mail arrives in an IMAP mailbox, the current message is now properly advanced by the 'next' command if it has been printed before. * If neither the host name nor the user's email address can be determined, no Message-ID fields are generated. * The empty string is now accepted as a message subject for sending. [11.11] released 10/13/04 * When sending messages that contain ASCII control characters but neither NULs nor non-ASCII characters, the quoted-printable MIME encoding is now used instead of 7bit. * A dangling pointer after a memory reallocation could result in a coredump when the junk mail database was updated by a 'good' or a 'junk' command. * The code for accessing the junk mail database has been made more machine- independent. It will not make a difference on most of the platforms on which nail runs. If your compiler does insert padding into C structs that contain only members of type char, you will have to create the junk mail database from scratch again, though. [11.10] released 10/3/04 * The creation of quoted-printable encoded MIME data was broken in 11.9. * Junk mail filter improvement: Do not ignore single-letter words; extract hostnames from URLs and store them separately. [11.9] released 10/2/04 * A Bayesian junk mail filter has been added. See the 'EXAMPLES' section of the manual page for an introduction. * Fixed an IMAP response parsing bug which caused nail to hang on messages that contained no text with many IMAP servers. * Empty lines within the command lists for account or macro definitions are now ignored. * An attempt to define a macro twice is now properly detected and results in an error message. * Nail was reported to hang sporadically when sending multipart messages on HP-UX. A possible reason for such problems on systems without /dev/urandom has been eliminated (Bugreport by Patrick Briggs). * Fixed a small internal stack buffer overflow that occured regardless of input data and lead to segmentation faults on some platforms when the header summary was printed, depending on the stack layout. * Nail now builds without socket (networking) support again. * Function declarations and definitions have been converted to ANSI C. This should cause no problems since the code used ANSI C features anyway before. [11.8] released 9/23/04 * Support for S/MIME has been added. Introductory documentation is available in the 'EXAMPLES' section of the manual page. * It is now possible to use Mozilla NSS instead of OpenSSL for both S/MIME and SSL/TLS. This makes it possible to use Mozilla to manage the certificate databases. See the INSTALL file for futher details. Note to redistributors: The new file nss.c is governed by the Mozilla Public License (MPL), so make sure that you comply to it (or delete that file and do not use NSS). * Certificate revocation lists (CRLs) can now be used with S/MIME and SSL/TLS in combination with OpenSSL (for NSS, no special support is necessary). * New 'remove' and 'rename' commands for mailboxes are available. * Variable names that contain an '@' are now converted to all-lowercase internally. Thus in variable names like disconnected-user@host, imap-auth-user@host, password-user@host, smtp-auth-user@host, the case of the user@host part does not matter anymore. [11.7] released 9/16/04 * A collapsed view of message threads was introduced. See the 'collapse' and 'uncollapse' commands, and the 'autocollapse' variable. * The 'z' command now always shifts the header summary by a full screen of messages and is not disturbed by deleted, hidden, or killed messages anymore. * When an 'account' command was executed and messages were sent to mbox when the old mailbox was quit, the mbox on the new account was used. Messages from the old mailbox now go to the mbox on the old account. * Nail did not properly keep the mark for the current message if messages were expunged on an IMAP server. * The 'move'/'Move' command now sets the current message to the first undeleted message following the last message it moved, like the 'delete' command. * The 'markanswered' variable has been fixed to be effective with the 'Reply' command too, and not to mark messages if the reply has been aborted by '~q' or '~x' or if a send error was detected. * If the initial connection setup to an IMAP server is interrupted or if authentication fails, folder hooks are no longer executed. * If the 'inc'/'newmail' commands were executed on an IMAP connection that was already closed by the server, nail did not catch SIGPIPE properly and was terminated. * IMAP or POP3 servers that erroneously send a Unix 'From ' line at the beginning of message headers are now handled gracefully. [11.6] released 9/7/04 * An IMAP performance improvement introduced in release 11.5 lead to an inconsistency in the internal states if a command like 'copy' was followed by another command that fetched a message text from the server. Due to an error in the IMAP response parser, this sometimes resulted in core dumps. * Support for the 'maildir' folder format was added. * The 'account' command was changed; its syntax and semantics are similar to the 'define' command now. This change is downwards compatible, and old 'account' definitions are still accepted. * If the account listing generated by the 'account' commands without an argument exceeds the screen height, the pager is invoked now. * It is not an error to 'unset' an undefined variable from within a 'call' command or folder hook now. * A 'defines' command was added to print the currently defined macros. * The header summary that is printed when new mail arrives erroneously included killed messages. * When a message in a multibyte character set like UTF-8 that was encoded as quoted-printable was printed and a multibyte character was separated by a '=' continued line, it was not decoded correctly. * The '&' addressing mode is now recognized in non-threaded mode too, although it has no special effect then. * When the 'thread', 'sort', 'unthread', or 'unsort' commands are called from within a folder hook, they do not print a header summary anymore. Also, the first new message is made the current message correctly now in this situation. [11.5] released 9/5/04 *************************************************************************** * A mailing list has been created to discuss future nail developments and * * problem reports. If you like to participate, you can subscribe at * * . Regardless * * of your subscriber state, bug reports etc. should be sent to the list * * address from now on. * *************************************************************************** * A new message addressing mode '(criteria)' is now available. It uses the IMAP SEARCH command if possible, and otherwise performs the equivalent actions locally. This addressing mode is of interest even for those people who do not use IMAP with nail because it adds powerful generic search methods, such as a way to search within message bodies. * The 'folders' command now really works with IMAP accounts. It also accepts an argument to list the subfolders of a specific folder. * The new 'headline' variable allows to customize the columns in the header summary. * The new 'attrlist' variable allows to customize the attributes column in the header summary. * The message flags 'Flagged' (F), 'Answered' (A), and 'Draft' (T) are now available, for IMAP mailboxes as well as for local folders. See the 'flag', 'unflag', 'answered', 'unanswered', 'draft', and 'undraft' commands and the 'markanswered' variable. * The 'kill'/'unkill'/'score' commands were added to make messages invisible on the user's wish. * It is now possible to define sequences of commands as macros, see the 'define', 'call', and 'undef' commands. Macros can also be automatically invoked when a folder is opened using the 'folder-hook' and 'folder-hook-fullname' variables. * The new 'move' and 'Move' commands mark messages for deletion when they have been successfully transferred to the target folder. * The new addressing mode '`' selects all messages that were included in the message list to the previous command. * The combination of the previous additions allows a score/filter file to be set up; this is described in the EXAMPLES section of the manual page. * New 'noop' command to send NOOP to POP3 or IMAP servers. * The IMAP UIDPLUS extension (RFC 2359) is now used if available on the server. It speeds up the IMAP cache when copying messages to IMAP folders; the message is copied directly to the cached target folder then and does not need to be downloaded again when it is accessed there. * When an IMAP SELECT command fails and a connection to a folder that is not located on the same server is made afterwards, the connection to the old server is properly terminated now. * Command lines like 'nail -A imap -f @mailbox' now work. * IMAP or POP3 user names that contain an '@' character, such as 'foo@bar' in 'imap://foo@bar@hostname', are now handled (Bugreport by Lars Kellogg-Stedman). * If the 'autoinc'/'newmail' variables are set and new mail arrives in threaded/sorted mode, no summary of old headers is printed anymore. * If new mail arrives and messages are expunged on an IMAP server at the same time, all of the new messages are now announced correctly. * The 'inc'/'newmail' commands now set the current message to the first new message again. This undoes the change made for version 11.1. * When the 'autoinc'/'newmail' variables are set and new mail arrives, the 'next' command or enter key prints the next message as usual if the current one has been printed; it printed the current message again in this situation. * IMAP performance improvement: When new mail arrives, or when messages have been expunged, metadata is fetched only for the new messages, instead of fetching it for all messages in the folder again as it was done before. * IMAP performance improvement: If the header of a message was already fetched from an IMAP server, nail now uses 'BODY.PEEK[TEXT]' when fetching the whole message and does not download it again. * IMAP performance improvement: The CLOSE command is now used instead of EXPUNGE when a mailbox is quit. * Cached IMAP performance improvement: When a mailbox is opened and messages are cached, their sizes and internal dates are taken from the cache. * The wcwidth() library function is now used to determine the width of multibyte characters if it is available. * If $MAIL does not exist and 'emptystart' is set, a proper error message is printed now, and a following 'account' command succeeds. * The SSL code compiles with older versions of OpenSSL (e.g. 0.9.6b) again (Bugreport by Lutz Horn). [11.4] released 8/29/04 * The SSL/X509 DNS name verification code was fixed; it used incorrect code from a book which sometimes caused segmentation faults (Bugreport by Lars Kellogg-Stedman). * The 'disconnect' command now accepts an optional message list specifying messages to be read into the IMAP cache before the connection is closed. * The new 'cache' command reads a list of messages into the IMAP cache. * IMAP BODY.PEEK[] is now used when fetching messages from the server, and the '\Seen' flag is set when the 'quit' command is executed. Thus an 'exit' command does not cause messages marked to be read. * The 'connect'/'online' commands now announce new messages that are found on the server. * The 'replyto' variable can now contain multiple addresses. * If the 'sort' command is used without arguments, the current sorting criterion is printed. * The 'sort', 'thread', 'unsort', and 'unthread' commands now only print a header summary if the 'header' variable is set. * The 'size' command has been fixed to print the full sizes of messages that have not yet been entirely read in IMAP and POP3 folders, instead of the sizes of the already downloaded parts. * Deleted messages remained in the cache until an IMAP folder was accessed a second time since 11.3. They are now deleted immediately when a folder is quit in online mode. * The configuration system now also checks for iconv() in libiconv if it is not found in one of the standard libraries (Matthias Andree). * Specifying LIBS on the make command line does now work with several make implementations of commercial Unices too (Bugreports by Matthias Andree, Matthew Sienkiewicz). [11.3] released 8/18/04 * When an IMAP mailbox is opened in threaded/sorted mode and new messages arrive or old ones are deleted, the internal thread structure is now properly rebuilt. The previous failure to do so made nail dump core in most cases. * The 'forward' command was fixed; it used an unitialized variable since release 11.2, possibly leading to segmentation faults (Bugreport by Bob Tennent). * The behavior of the 'autoinc'/'newmail' variables regarding IMAP folders was changed. If both variables are unset, announcements are printed only if messages have been deleted on the server. * The 'new'/'unread' commands now also work with IMAP folders. * When a message is deleted or saved in disconnected mode, its cache entry is not removed until the message is actually removed from the server in online mode. This in particular means that saved or 'mbox'ed messages are still available in IMAP inboxes in disconnected mode until they have reached their target folder in online mode. * When new messages were received in an IMAP mailbox after messages had been deleted in a previously visited IMAP mailbox, 'Expunged x messages' was erroneously printed. [11.2] released 8/15/04 * A message thread-oriented mode is now optionally available; cf. the 'thread' and 'unthread' commands, the 'autothread' variable, and addressing changes. * It is also possible to sort messages by various other criteria; cf. the 'sort' and 'unsort' commands, the 'autosort' variable, and addressing changes. * New 'fullnames' variable: When replying to a message, full names for the recipients are included if it is set, instead of using the plain email address. * New commands 'disconnect' and 'connect'/'online' to switch to disconnected IMAP mode and back while retaining the current mailbox. * IMAP GSSAPI authentication was implemented; it has been successfully compiled with MIT, Solaris, and HP-UX GSS libraries. See the 'imap-auth' and 'imap-auth-user@host' variables. * CRAM-MD5 authentication was implemented for IMAP and SMTP. See the 'imap-auth' and 'smtp-auth' variables. * POP3 APOP authentication is now supported; see 'pop3-use-apop'. * STARTTLS is now also supported for IMAP and POP3; see 'imap-use-starttls' and 'pop3-use-starttls'. * Autodetection of the terminal character set works again (did not work since 10.8). * The 'showlast' variable works again; it had no effect since version 11.0. * Made it compile using the HP-UX C compiler again (did not work since 11.0). * Standard input is now redirected to /dev/null in the child process that contacts the MTA. A result is that ssl-verify=ask has the same effect as ssl-verify=strict for SMTP servers, and does not put the terminal in an indeterminable state anymore. * The initial connection to IMAP or POP3 servers is now made before the previous mailbox (if any) is quit. Thus if a server is unreachable, nail keeps the previous mailbox open instead of getting in a mailbox-less state. * Stale cache entries for messages deleted on the server were not removed in some circumstances. * The 'inc'/'newmail' commands are now ignored in disconnected mode and no longer display errors about hidden (uncached) messages. * The cache now compiles and works on systems that lack fchdir(), such as some Cray machines (Thanks to Cray-Cyber.org). * The defaults for /etc/nail.rc have been updated. This will have no effect on existing installations unless the file is copied by hand. * Nail does not hang anymore in disconnected mode if a message addressed with ^ or $ is not cached. [11.1] released 8/8/04 * IMAP mailboxes can now be cached locally. See the string variable 'imap-cache' in the manual page. The cache can also be used for disconnected operation mode, see 'disconnected' and also the '-D' command line option. * When nail is notified by the server that messages have been expunged, the user is informed about this condition even if new messages have also arrived. * When the group of variables for an 'account' command contains a variable prefixed by 'no...' and the related variable is not set, it is not an error to unset it. * The 'headers' command has been made faster when operating on an IMAP mailbox by bundling IMAP requests. * When a 'folder' command was executed to change from an uncompressed file to a compressed one, the latter was incorrectly detected as being read-only. * Unless one of the 'autoinc' or 'newmail' variables is set, nail does not actively check for new messages in the current IMAP mailbox before each prompt. New messages may nevertheless be detected after any other IMAP command has been issued, so the effect is mostly that not every nail command causes an IMAP command to be sent. Also if 'autoinc' or 'newmail' are set to the value "noimap", they are ignored for IMAP mailboxes but still effective for local folders. * The 'inc' and 'newmail' commands now use the same method to determine the location of the current message as the 'folder' command does. That means if there has been new mail before that is still unread when newer mail arrives, the older messages are displayed first. * Nail does not generate 'Sender:' fields anymore. * If OpenSSL is not available, STARTTLS support is not built, and nail compiles again without OpenSSL. [11.0] released 7/30/04 * IMAP support has been added. It should considered to be in beta state now, but should not have any disturbing effects on other code unless it is actually used. (So the version jump does not require vendors to continue to ship older releases.) To get started with IMAP, see the manual page for the 'account', 'folder', and 'imap' commands, and for the 'folder' variable, as well as the EXAMPLES and NOTES sections. * Support for SMTP AUTH LOGIN was added (contributed by John Fawcett). * It is now possible to encrypt SMTP with SSL/TLS, using the STARTTLS method as well as with the SMTPS variant. * New 'account' command to set groups of variables. * Line and column limits (used e.g. for the header summary) are updated when the size of the terminal is changed (handling SIGWINCH). * If saving an outgoing message to the folder given in the 'record' variable fails, the message is not sent but put in 'dead.letter' instead. This rarely happens with local record folders, but is of concern if 'record' refers to an IMAP mailbox. * For addresses specified with the ~b, ~c, ~h, and ~t tilde escapes or by the 'editheaders' method, if a comma, parenthesis, angle bracket, or quoting character appears, only the comma is accepted as an address separator. Otherwise, whitespace separates addresses as elsewhere. This allows the insertion of comments in recipient addresses and more closely matches the behavior of System V mailx. (Thanks to Ryan Lovett and Hilko Bengen.) * An internal version of getopt() is now used to properly work around system defects such as the option reordering on GNU libc based systems. If you really prefer the libc version, remove getopt.o from the list of objects in the Makefile. (Note that nail already worked around the glibc problem, so unless _your_ patches changed its behavior, use the new internal getopt() to remain compatible.) * The undocumented facility to pass sendmail options on the command line of nail has been removed because it was not compatible with POSIX and System V. If you need to pass options to sendmail, create a shell script which invokes it appropriately and let the nail variable 'sendmail' point to its location. * Fixed the text of some usage messages (thanks to Christian Reiber). [10.8] released 6/28/04 * Support for IPv6 is not built by default anymore. There are just too many hosts on which it doesn't work while the IPv4 functions work fine. If you have use for IPv6 support and know that it works, enable it in the Makefile. * autoconf and automake are no longer used for building. See the file 'INSTALL' which describes the transition of existing build setups. * The --enable-all-chars configuration option is no longer available. A run-time nail option variable 'print-all-chars' has been introduced instead. * Nail can now handle folders in compressed mbox format. If a file name given on the command line or with the 'folder', 'copy', and 'save' commands ends with '.gz' or '.bz2', gzip(1) or bzip2(1) are executed when reading and writing to it. Likewise, if a folder 'name' does not exist, but either 'name.gz' or 'name.bz2' are found, the compressed folder is used. To make use of compressed folders, just compress the respective mbox format file. * If the variable SHELL is not set, the default shell is now /bin/sh instead of csh. * If the variable EDITOR is not set, the default editor is now 'ed', as specified by SUSv3. * Introduced an 'ssl-verify-user@host' variable as an account-specific variant of 'ssl-verify'. * Introduced an 'ssl-method-user@host' variable as an account-specific variant of 'ssl-method'. [10.7] released 3/19/04 * The IEEE and The Open Group have granted permission to use parts of the POSIX standard text for the documentation of 'nail'. See the file 'COPYING' for general terms. The manual page has been updated accordingly. * If the variable 'sendwait' is set and the mail transfer agent returns a non-zero exit status, the exit status of nail will also be non-zero (Christian Reiber). * For the first (textual) part of a multipart message, create a 'Content-Disposition: inline' header field to make it clear to other MUAs that this part should be displayed directly (suggested by Alexandros Gougousoudis). * Base64 encoded messages were not decoded correctly on machines where the 'char' type is unsigned by default (Fix by Benjamin C. W. Sittler, reported by Hilko Bengen). * Any address that contains a '@' character is treated as an internet mail address (even if it contains a '/' character before). * Fixed the 'autocc' and 'autobcc' variables to really accept address lists instead of single names. * If a message is sent to both addressed recipients and local folders, don't pass the file names with the SMTP client. * The ~w command appends the message text to an existing file instead of refusing to write, as POSIX.2 states (Bugreport by Bob Tennent). * Escape sequences in quoted arguments to the alias, unset, chdir, cd, source, set, group, ungroup, unalias, file, folder, if, else, endif, alternates, ignore, discard, retain, saveignore, savediscard, saveretain, unignore, unretain, unsaveignore, unsaveretain, shortcut, and unshortcut commands are no longer handled specially. This is for compatibility with System V mailx and POSIX standards. * The echo command accepts the same escape sequences as specified for echo(1) in recent Open Group standards. * Only the \t and \n sequences are handled for the ~a, ~A and ~i tilde commands and the insertion of the MAILX_HEAD and MAILX_TAIL strings (Thanks to Bob Tennent). * Solaris: When the MAILX_TAIL variable was set and a ~p command was issued before the message was sent, parts of the text were sent twice. * Introduced a 'hostname' variable to override the value obtained from uname(2) and getaddrinfo(3) (William Bader). [10.6] released 11/15/03 * A nested MIME multipart message with a sub-part piped through an external program (such as HTML with w3m) caused nail to abort after SIGPIPE if the PAGER command terminated before reading the whole message. * A 'next' command following a 'hold' command displays the next message after the one the 'hold' applies to (Bugreport by Mike Sipser). This might not be exactly what POSIX specifies, but it makes sense and is consistent with traditional behavior. If you actually favor 'next' not to advance after 'hold', contact me and I'll add a configuration option for this. * If the value of the 'record' variable started with an environment variable reference such as '$HOME' or with a tilde and the 'outfolder' variable was set, it was not expanded correctly (Bugreport by Volker Kuhlmann). [10.5] released 4/27/03 * A command line argument specifying a To-address can contain multiple recipients separated by commas (Werner Fink). * 'showname' variable (based on code contributed by Stephen Isard). * Display nonprintable characters in mail addresses as question marks. * Fixed MIME encoding of user's From address with respect to quoted characters. * 'autobcc' and 'autocc' variables. * The effects of the 'bsdcompat' variable are now individually configurable. This introduces the 'bsdflags', 'bsdheadline', 'bsdset', 'bsdannounce', 'askatend', 'bsdorder', and 'bsdmsgs' variables. * The current message pointer could be garbled when 'newmail' was set and new messages appeared (Fix by Stephen Isard). [10.4] released 1/19/03 * A missing #ifdef HAVE_ICONV caused build errors on platforms without iconv() support (J.A. Neitzel, Jens Schleusener, Lars Kellogg-Stedman, Felicia Neff, Chris Pinnock). * Extraction of parts from header fields with more than 2560 characters fixed (Lukasz Sznuk, William Cherry). * Source archive name in nail.spec corrected (Didar Hussain). * Fixed segmentation violation when encountering multipart header fields that contain no ':' character (William Cherry). * Respect the 'allnet' variable when comparing strings in message lists. * IPv6 support (Jun-ichiro itojun Hagino). [10.3] released 11/29/02 * Fixed a buffer overflow that occured with iconv() support disabled (Stan Tobias). * MIME encoding of '(non-ASCII-string' comment strings in address fields fixed (non-ASCII-string immediately following the opening parenthesis). * Convert header fields to outgoing character set even if the body contains ASCII characters only. * Build properly on platforms without socket support (J.A. Neitzel). * Don't require towupper() with mbtowc() (J.A. Neitzel). Searches for wide characters in subject fields are not case-insensitive without towupper(). * Some OpenBSD and NetBSD versions don't adjust the kernel file offset properly after a 'fflush(); rewind()' sequence, violating POSIX.1. This caused empty mails to be sent if the 'record' variable was set. The code now works around the problem (Thanks to J.A. Neitzel). * NetBSD lacks gethostent(), causing the configure check for socket support to fail. gethostbyname() is now used instead (Thanks to J.A. Neitzel). [10.2] released 11/23/02 * Fixed a NULL pointer dereference that occured if the 'showto' variable was used without the 'alternates' command (Bugreport by Bob Tennent). * POP3 client with SSL/TLS support. See the description of the 'folder' command, the 'EXAMPLES' and 'NOTES' sections in the manual page, and the description of the new variables 'password-user@host, 'pop3-keepalive', 'ssl-no-default-ca', 'ssl-v2-allow', 'ssl-verify', 'ssl-ca-dir', 'ssl-ca-file', 'ssl-cert', 'ssl-cert-user@host', 'ssl-key', 'ssl-key-user@host', 'ssl-method', 'ssl-cipher-list' 'ssl-rand-egd', 'ssl-rand-file'. You need to execute './configure --with-openssl' to compile with SSL support. * Respect assignments to the MAIL variable in startup scripts. * Secondary mailboxes with %:folder (see the 'folder' command). * 'shortcut' and 'unshortcut' commands. * NAIL_EXTRA_RC variable. * ',' address (matches the parent of the current message). * ';' address (matches the message that was previously the current message). * Recognize MIME in header field searches with '/'. * The 'save' and 'copy' commands use the mbox file if invoked without an argument. * Use wordexp() to expand special characters in filenames if available. $SHELL-specific metacharacters are thus no longer recognized. * The output of the 'from' command is piped through the pager if it exceeds the screen height. * 'datefield' variable. * 'editheaders' variable (contributed by Stephen Isard). * '-t' command line option. * 'term' condition for 'if' command (contributed by Stephen Isard). * Don't prepend a message with a blank line when saving if a blank line is already present at the end of the file. * Fixed quoting for command argument of 'pipe' (Stephen Isard). * 'save' and 'copy' commands accept quoted file names with embedded whitespace. * Allow '|shell-cmd' (Stephen Isard). * 'piperaw' variable (Stephen Isard). * If 'askcc' or 'askbcc' are set and 'bsdcompat' is not set, the user is asked for additional recipients before composing the message starts. * Don't append an additional newline to filenames in recipient lists. * Message lines starting with '>' were truncated by the 'forward' command. * Don't rename old Resent-xxx fields when creating new ones. * Deletion of attachments with '~@' fixed. * Don't mark the saved copy of an unread message as read. * Fixed use of a char variable for getc() (Arthur Korn). [10.1] released 10/14/02 * POSIX.2 leaves the characters following 'From ' unspecified, so the presence of sender or date strings is no longer required to separate messages in a folder. * 'inc' and 'newmail' commands, 'autoinc' and 'newmail' variables. * Don't use iconv() if a message contains us-ascii characters only (Problem reported by Thomas Fieseler). * Don't report 'Held n messages in mailfile' if n == 0. * Don't report a 'Fatal error in process' with '!' unless bsdcompat is set. * Missing fflush() caused empty mails with some GNU libc versions to be sent, unless SMTP was used (Problem reported by Thomas Fieseler). [10.0] released 9/29/02 * Base64 decoding was calling malloc(0) which caused "Out of memory" errors on some platforms (Bugreport by Stan Tobias). * Install nail.rc in the place specified with --with-rcfile (Bugreport by Jeffrey Neitzel) * Code cleanup in send.c (Contribution by Stan Tobias). * When writing attachment data, the 'w' command will no longer append to a file, but truncate and overwrite it. * Extract filenames from obsolete 'Content-Type: ...; name=file' fields (Stan Tobias). * Delete directory components from attachment filenames before saving (Fix by Stan Tobias). * NAILRC variable (based on a suggestion by Stan Tobias). * "bsdcompat" and "prompt" variables; prompt defaults to "? ". * "allnet", "debug", "flipr", "outfolder", "sendwait", and "showto" variables * "bang" variable. NOTE: Previous versions behaved as if this variable had been set. * ~a and ~A tilde escapes with standard semantics. * -F command line option, "Mail", "followup", "Followup", "Copy" and "Save" commands. NOTE: This changes the abbreviation of the "folder" command to "fold". * "set nofoo" unsets the variable "foo". The variables "nosave" and "noheader" have therefore changed in their positive counterparts and are now set by default. * The pg command is now the default pager unless bsdcompat is set. * Line and character counts are printed for copy and save commands. * MAILX_HEAD and MAILX_TAIL variables. * LC_MESSAGES support. * -I option prints 'Newsgroups:' field in header summary now. NOTE: The functionality of the old -I option, "interactive", is no longer available as an option. Use something like "env interactive= nail ..." instead. If only tilde escapes in send mode are needed, the new -~ option can be used. * The optional name argument in combination with the -f option is now evaluated as a separate operand, not as an argument to -f. This enables usage as "nail -fin name", but "nail -f name -in" is no longer valid. * Pipe the output of ~p through the PAGER if the text is long (Bugreport by Stan Tobias). * The output format of the "set" command is changed to 'variable="value"' if bsdcompat is not set. * Pipe the output of the "set" command through the PAGER if it is long. * Headers are not printed automatically after executing a "folder" command if bsdcompat is not set. * The "next" command was fixed according to the remark in the POSIX rationale not to skip a message e. g. after "delete". * -B, -h and -~ command line options. * Lines in message files can now be of arbitrary length and can contain NUL characters. * -r disables tilde escapes; -r flag and address is passed to sendmail. * followupall, followupsender, replyall, replysender, respondall, and respondsender commands. * New, new, and Unread commands (are all aliases to unread command). * unalias command implemented (had been documented since 4.4BSD). * unignore, unretain, unsaveignore, and unsaveretain commands. * If data from standard input contains NUL characters, no character conversions are performed and it is sent in base64 encoding. * Non-binary data with long lines is sent in quoted-printable encoding. * Fixed segmentation violation when using the "folder" command after an interrupt signal was catched. * Resent-Reply-To: header field is no longer generated for conformance with RFC 2822. * Fixed -u user option and %user special file name not to ignore 'user'. * The 'Reply' command uses the 'Reply-To:' header field in favor of 'From:'; the 'reply' command adds recipients given in the 'To:' and 'Cc:' fields if a 'Reply-To:' field was present in the message. This means that 'Reply-To:' just overrides 'From:' without a special effect on the 'reply' and 'Reply' commands. * When grabbing headers e. g. with the '~h' command, a comma is printed between names if bsdcompat is not set. * RPM spec file (contributed by Didar Hussain). [9.31] released 6/18/02 * New editing mechanism for attachment list: ~@ tilde escape. * The ~a tilde escape is obsoleted by ~@ and has been removed. * Header fields starting with =? are only interpreted as RFC 2047 fields if they are entirely valid (Bugreport by Russell Kroll). * Sender addresses in message lists (as in 'delete from@host') match only the exact from address. * Added ~x tilde escape (abort message composition without saving dead.letter). * Fixed recognition of RFC 2045 parameter attributes (Bugreport by Russell Kroll). * The "for" command now inserts an intact "Resent-Reply-To:" header field. [9.30] released 2/20/02 * Avoid to prepend quote prefix twice when replying to multipart messages. * Fixed a problem with empty Content-type fields in multipart messages. * Allow name lists for -b and -c command line options (Bugreport by Winfried Szukalski). * Added a --with-rcfile option to configure for nail.rc. * Added the "showlast" option (Contributed by Jay Nospam). * Properly respect TMPDIR; create a new name for each temporary file. [9.29] released 12/10/01 * Fixed a SIGPIPE problem concerning the PAGER. * Avoid to display parts of next message when encountering masked From lines (Bugreport by Russell Kroll). * Implemented pipe-content/subcontent MIME display mechanism (based on a suggestion by Bob Tennent). * Respect continued MIME quoted-printable lines in non-text content. [9.28] released 10/23/01 * Fixed a crash occuring at unterminated RFC 2047 header parts (Bugreport by Bruno Haible). * Fixed a crash that occured when replying to a message with empty subject (Bugreport by Russell Kroll). * Send attachments in base64 encoding if a line exceeds 950 characters (Bugreport by Rolf Jakob). [9.27] released 5/13/01 * Check addresses when they are entered and drop invalid ones. * Supress glibc's getopt(3) argument reordering. * Fix handling of last entry on a mime.types file line (bug introduced in 9.24) (Bugreport by Allan Peda). * Improved handling of proprietary character set names on SVr4 iconv. [9.26] released 2/17/01 * Avoid to prepend or append multiple newline characters to the record folder. * Handle unquoted multipart boundaries again (Bugreport by Bob Tennent). * Help texts are compiled into the binary. Old files can be deleted (based on a comment by Christian Weisgerber). * nail.rc gets installed in /etc and is not overwritten if it exists. * Check for iswprint() before using wide character code (Bugreport by Hubertus Krogmann). * Use a non-zero exit status if a message could not get formatted in send mode (Bugreport by Allan Peda). [9.25] released 1/25/01 * Fixed the problems some people had when viewing multipart messages (Bugreport by Thomas Fieseler and Russell Kroll). * Added the -r startup option to specify a From address. * Make fallback paths for csh, more, ex and vi configureable (Suggested by Arthur Korn). * Remove fcntl lock of mailbox while waiting for dot lock (Problem reported by Arthur Korn). [9.24] released 1/18/01 * Use nl_langinfo to get the terminal's character set. * Add an In-Reply-To header field in replies. * Correct user's mime type file to ~/.mime.types as documented (Bugreport by Allan Peda). * Build even if no sendmail installation is detected (Bugreport by Gabor Z. Papp). * Adjust string length if an invalid multibyte character was replaced by '?'. * Properly display lines that contain an invalid multibyte character sequence. * Handle tabulators in MIME headers correctly (Bugreport by Russell Kroll). * Drop invalid entries for the References field instead of refusing to send (Bugreport by Bob Tennent). * Do not refuse valid domain literals in addresses (Bugreport by Bob Tennent). [9.23] released 11/15/00 * Added the ~' * Proper handling of nested multipart messages implemented * We now ignore the MIME-Version header field and look at the Content-Type field instead * Can now compile on SVR4 systems using -lucb * Changed generation of multipart boundaries * Sending MIME messages to files now works * Fixed core dump if variable `encoding' was unset (Bugreport by Alexander Shelkovich) [9.0] released 2000-03-20 * Initial release heirloom-mailx-12.5/INSTALL000066400000000000000000000247401155563371200154370ustar00rootroot00000000000000How to configure and install mailx ================================== Quick start ----------- make make install No 'configure' step is necessary anymore. If 'make install' fails because no /usr/ucb/install program is present, try make install UCBINSTALL=/usr/bin/install Detailed description -------------------- Path names are configurable as Makefile variables: PREFIX The default Makefile puts BINDIR and MANDIR below this. BINDIR The mailx binary is installed there. MANDIR The mailx manual page is put in $(MANDIR)/man1. SYSCONFDIR nail.rc is installed in this directory unless it already exists. MAILSPOOL The directory where the mail files of users are stored under their respective names and in mbox format. If only POP3 is used to read mail, the value does not matter. SENDMAIL Path to the de-facto standard /usr/lib/sendmail interface normally offered by Unix MTAs. If only SMTP is used to send mail, the value does not matter. Note that if you want to use the '-r' option in mailx, the sendmail interface must support the '-r' option too. DESTDIR Prepended to BINDIR, MANDIR etc. at 'make install'. Mostly useful for package building. UCBINSTALL Path name to a BSD-style install, like /usr/ucb/install on SVR4 or GNU install. STRIP A command to strip the binary at 'make install'. Use the do-nothing command ':' to avoid stripping. Mailx uses a simple shell script named 'makeconfig' to check for header files and library functions. It is automatically invoked by 'make all'. The script generates three files, 'config.h', 'LIBS', and 'config.log'. 'config.h' is included as usual, 'LIBS' contains options for the linker, and 'config.log' contains all test programs and test results. Neither of these files is changed by make once it exists until 'make mrproper' is run, so they can be edited by hand without much fear of losing them. Useful Makefile targets: all Initiates the build; just type 'make'. install Installs the binary and the manual page. If the systemwide configuration file is not present already, is is also installed. clean Removes object files. mrproper Removes object files and the configuration results. Makefile variables to control the build process: CFLAGS Flags for the C compiler, such as '-O'. CPPFLAGS Flags for the C preprocessor. Some versions of glibc on Linux need '-D_GNU_SOURCE' here, or wcwidth() will not be available and multibyte characters cannot be displayed correctly. INCLUDES A list of additional include file directories, as '-I/usr/local/include'. Use this to locate the include files for NSS, OpenSSL, or iconv(3), if they are not present in the standard include directories. Also, some versions of RedHat Linux need -I/usr/kerberos/include to compile with OpenSSL, or to compile with GSSAPI authentication included. LDFLAGS Flags for the linker. To compile with GSSAPI authentication included, some RedHat versions need -L/usr/kerberos/lib. Also use this to specify the NSS or OpenSSL library path. LIBS A list of additional libraries such as '-lfoo'. -lsocket, -lnsl, -lssl, -lcrypto, -liconv, and NSS libraries are automatically included by makeconfig and should not be put into this. As usual with Makefile variables, you can pass these values to make in the environment or on the command line. Thus if you want to avoid to edit the Makefile, you can create a shell script to invoke make, or set flags of general use (such as CFLAGS) in .profile. Note that passing flags on the command line does not override those specified in the Makefile itself with several commercial versions of make in combination with the recursive make calls that are executed at configuration time. Transition remarks concerning the old configure system ====================================================== * --prefix and other common configure options: Path names can be specified in the Makefile or as assignment arguments to make. * --with-openssl: No longer available. Mailx is always built with OpenSSL support if possible. If you really want to have a mailx binary without OpenSSL support, edit config.h after running 'make config.h'. * --enable-all-chars: No longer available at compile time. 'print-all-chars' can be set in mailx in those cases where use of this option was necessary. * --with-rcfile, --with-mailspool, --with-sendmail: These path names can be set in the Makefile or as arguments to make. * --with-csh: No longer used. Mailx now uses /bin/sh for executing commands if the SHELL variable is not set. * --with-more, --with-pg, --with-ed, --with-vi: No longer available. If the PAGER, EDITOR, or VISUAL variables are not set, the executable is looked up using $PATH. * --with-catname=NAME: Contact the author if you want usable message catalogue support. In short, if you were using -------- ./configure --prefix=/opt/nail --with-rcfile=/opt/nail/etc/nail.rc up to now, replace it by make PREFIX=/opt/mailx SYSCONFDIR=/opt/mailx/etc The DESTDIR variable for building packages is available as before. If you intend to build mailx packages, a look at 'mailx.spec' might be helpful - even if you don't use RPM, the process is likely to be similar. Why autoconf/automake are no longer used ======================================== Autoconf and automake are systems of considerable complexity which one must know well to make real use of them. The autoconf/automake scripts for mailx were supplied by a contributor in 2000 and were out of date as of 2004; they wouldn't work with recent versions of automake, in particular. But I'm not very interested in learning automake myself, and I'm absolutely not interested in keeping such knowledge up-to-date with new automake versions. Furthermore, I've made some horrible hacks for the autoconf/automake scripts in previous versions (such as --with-rcfile) because once one doesn't like some particular behavior of them, working around can be difficult. Such hacks are unnecessary now. The new build system needs approximately the same number of lines as just the maintainer-supplied portions of the old autoconf/automake system did. Since it is written in standard languages (shell and make), any Unix developer can read and understand it; to make adjustments to it, no special versions of third-party tools are needed. Every serious developer should understand the advantage (except perhaps some GNU addicts who have learned and installed all versions of automake anyway). For the user who just wants to build mailx, the new system is not more complicated than the old one; transition mostly involves passing path names to make instead of passing them to configure. (It's not even an argument that the user needs to know the names of Makefile variables; --prefix didn't work as designed by the GNU folks with the old system anyway.) In short, everything is better now. Building S/MIME and SSL/TLS support using Mozilla NSS ===================================================== It is possible to build encryption support using Mozilla NSS instead of OpenSSL. Doing so has both advantages and disadvantages: - With NSS, mailx can use the same key and certificate databases as the Mozilla applications (Mozilla Suite, Firefox, Thunderbird). This makes it possible to install and configure certificates at one central location. Note that running mailx and Mozilla at the same time from one certificate directory may result in 'Bad database' errors in mailx if Mozilla modifies the configuration files (mailx never modifies them). If you encounter such problems, create a copy of the .db files for exclusive use with mailx. - OpenSSL offers more transparent control over certificates. While it is possible to modify the NSS databases using command line utilities (see ), it is certainly easier to use PEM files along with descriptions in OpenSSL if direct control is desired (e.g. for batch use). - NSS supports S/MIME in both versions 2 and 3, OpenSSL currently only supports version 2. - Building OpenSSL is much easier than building NSS for use with non-Mozilla applications like mailx. To build using NSS, you need both the NSS and the NSPR libraries/include files from the Mozilla project. If you are using a Linux distribution, chances are that you can install appropriate RPMs from your vendor, e.g. seamonkey-nspr-1.0.5-0.1.el4.centos4 seamonkey-nspr-devel-1.0.5-0.1.el4.centos4 seamonkey-nss-1.0.5-0.1.el4.centos4 seamonkey-nss-devel-1.0.5-0.1.el4.centos4 for CentOS 4. Note that you can install these without installing the RPM for the Mozilla main application. Thus it is no problem if you prefer to use a more recent Mozilla installation in /opt or so. NSS and NSPR are not updated as often as the Mozilla end-user applications in recent times (but you should watch for possible future security fixes regardless, of course.) If you have these RPMs installed, you can uncomment the marked lines in either the Makefile or in mailx.spec. Mailx should then build cleanly with NSS support included, and that's all. On recent Solaris versions, NSS and NSPR are available in the packages SUNWmoznss, SUNWmoznss-devel, SUNWmoznspr, and SUNWmoznspr-devel. If these packages are installed, the following make variables activate NSS support: INCLUDES=-I/usr/sfw/include/mozilla/nspr -I/usr/sfw/include/mozilla/nss LDFLAGS=-L/usr/sfw/lib/mozilla -R/usr/sfw/lib/mozilla If you want to use NSS without RPMS, you can get binary or source .tar.gz archives from the Mozilla project pages: Or you can use the files from a Mozilla application build directory if you are building Mozilla Suite, Firefox, Thunderbird etc. from source. Just set the INCLUDES and LDFLAGS variables appropriately. If both NSS and OpenSSL are available, NSS is used. This is just because NSS is normally not found without special action, and thus the build process simply assumes that you want to build NSS if it manages to build the test executable. Gunnar Ritter 12/25/06 heirloom-mailx-12.5/Makefile000066400000000000000000000101621155563371200160370ustar00rootroot00000000000000# # Makefile for mailx # # # See the file INSTALL if you need help. # PREFIX = /usr/local BINDIR = $(PREFIX)/bin MANDIR = $(PREFIX)/share/man SYSCONFDIR = /etc MAILRC = $(SYSCONFDIR)/nail.rc MAILSPOOL = /var/mail SENDMAIL = /usr/lib/sendmail DESTDIR = UCBINSTALL = /usr/ucb/install # Define compiler, preprocessor, and linker flags here. # Note that some Linux/glibc versions need -D_GNU_SOURCE in CPPFLAGS, or # wcwidth() will not be available and multibyte characters will not be # displayed correctly. #CFLAGS = #CPPFLAGS = #LDFLAGS = #WARN = -Wall -Wno-parentheses -Werror # Some RedHat versions need INCLUDES = -I/usr/kerberos/include to compile # with OpenSSL, or to compile with GSSAPI authentication included. In the # latter case, they also need LDFLAGS = -L/usr/kerberos/lib. #INCLUDES = -I/usr/kerberos/include #LDFLAGS = -L/usr/kerberos/lib # If you want to include SSL support using Mozilla NSS instead of OpenSSL, # set something like the following paths. (You might also need to set LDFLAGS). #MOZINC = /usr/include/mozilla-seamonkey-1.0.5 #INCLUDES = -I$(MOZINC)/nspr -I$(MOZINC)/nss # These paths are suitable to activate NSS support on Solaris, provided that # the packages SUNWmoznss, SUNWmoznss-devel, SUNWmoznspr, and SUNWmoznspr-devel # are installed. #MOZINC = /usr/sfw/include/mozilla #MOZLIB = /usr/sfw/lib/mozilla #INCLUDES = -I$(MOZINC)/nspr -I$(MOZINC)/nss #LDFLAGS = -L$(MOZLIB) -R$(MOZLIB) SHELL = /bin/sh # If you know that the IPv6 functions work on your machine, you can enable # them here. #IPv6 = -DHAVE_IPv6_FUNCS # # Binaries are stripped with this command after installation. # STRIP = strip ########################################################################### ########################################################################### # You should really know what you do if you change anything below this line ########################################################################### ########################################################################### FEATURES = -DMAILRC='"$(MAILRC)"' -DMAILSPOOL='"$(MAILSPOOL)"' \ -DSENDMAIL='"$(SENDMAIL)"' $(IPv6) OBJ = aux.o base64.o cache.o cmd1.o cmd2.o cmd3.o cmdtab.o collect.o \ dotlock.o edit.o fio.o getname.o getopt.o head.o hmac.o \ imap.o imap_search.o junk.o lex.o list.o lzw.o \ macro.o maildir.o main.o md5.o mime.o names.o nss.o \ openssl.o pop3.o popen.o quit.o \ send.o sendout.o smtp.o ssl.o strings.o temp.o thread.o tty.o \ v7.local.o vars.o \ version.o .SUFFIXES: .o .c .x .c.o: $(CC) $(CFLAGS) $(CPPFLAGS) $(FEATURES) $(INCLUDES) $(WARN) -c $< .c.x: $(CC) $(CFLAGS) $(CPPFLAGS) $(FEATURES) $(INCLUDES) $(WARN) -E $< >$@ .c: $(CC) $(CFLAGS) $(CPPFLAGS) $(FEATURES) $(INCLUDES) $(WARN) \ $(LDFLAGS) $< `grep '^[^#]' LIBS` $(LIBS) -o $@ all: mailx mailx: $(OBJ) LIBS $(CC) $(LDFLAGS) $(OBJ) `grep '^[^#]' LIBS` $(LIBS) -o mailx $(OBJ): config.h def.h extern.h glob.h rcv.h imap.o: imap_gssapi.c md5.o imap.o hmac.o smtp.o aux.o pop3.o junk.o: md5.h nss.o: nsserr.c config.h LIBS: makeconfig $(SHELL) ./makeconfig install: all test -d $(DESTDIR)$(BINDIR) || mkdir -p $(DESTDIR)$(BINDIR) $(UCBINSTALL) -c mailx $(DESTDIR)$(BINDIR)/mailx $(STRIP) $(DESTDIR)$(BINDIR)/mailx test -d $(DESTDIR)$(MANDIR)/man1 || mkdir -p $(DESTDIR)$(MANDIR)/man1 $(UCBINSTALL) -c -m 644 mailx.1 $(DESTDIR)$(MANDIR)/man1/mailx.1 test -d $(DESTDIR)$(SYSCONFDIR) || mkdir -p $(DESTDIR)$(SYSCONFDIR) test -f $(DESTDIR)$(MAILRC) || \ $(UCBINSTALL) -c -m 644 nail.rc $(DESTDIR)$(MAILRC) clean: rm -f $(OBJ) mailx *~ core log mrproper: clean rm -f config.h config.log LIBS PKGROOT = /var/tmp/mailx PKGTEMP = /var/tmp PKGPROTO = pkgproto mailx.pkg: all rm -rf $(PKGROOT) mkdir -p $(PKGROOT) $(MAKE) DESTDIR=$(PKGROOT) install rm -f $(PKGPROTO) echo 'i pkginfo' >$(PKGPROTO) (cd $(PKGROOT) && find . -print | pkgproto) | >>$(PKGPROTO) sed 's:^\([df] [^ ]* [^ ]* [^ ]*\) .*:\1 root root:; s:^f\( [^ ]* etc/\):v \1:; s:^f\( [^ ]* var/\):v \1:; s:^\(s [^ ]* [^ ]*=\)\([^/]\):\1./\2:' rm -rf $(PKGTEMP)/$@ pkgmk -a `uname -m` -d $(PKGTEMP) -r $(PKGROOT) -f $(PKGPROTO) $@ pkgtrans -o -s $(PKGTEMP) `pwd`/$@ $@ rm -rf $(PKGROOT) $(PKGPROTO) $(PKGTEMP)/$@ heirloom-mailx-12.5/README000066400000000000000000000151461155563371200152660ustar00rootroot00000000000000Welcome to Heirloom mailx! ========================== Mailx is derived from Berkeley Mail and is intended provide the functionality of the POSIX mailx command with additional support for MIME, IMAP, POP3, SMTP, and S/MIME. It provides enhanced features for interactive use, such as caching and disconnected operation for IMAP, message threading, scoring, and filtering. It is also usable as a mail batch language, both for sending and receiving mail. Until March 2006, this project has been developed under the name "nail"; it is integrated into the Heirloom project now. The old name will persist at some places. If you were calling the program under the name "nail" and want to continue to do so, create a symbolic link to the mailx binary. New releases of mailx are announced on Freshmeat. If you want to get notified by email on each release, use their subscription service at . The project homepage is currently at . How to build ============ To compile and install mailx, look at the file 'INSTALL'. You can also build mailx RPMs using 'rpmbuild -tb mailx-.tar.bz2'. You should always install the template for the system-wide configuration file. If this is not possible because you lack the necessary permissions, integrate its contents into your ~/.mailrc. This is because some of the built-in defaults are not appropriate anymore for the Unix platforms of today, but are still being kept for compatibility. Mailx has been built successfully in the following environments using the current configuration system: Linux Kernel 2.0 and above; libc4, libc5, glibc 2.2 and above, diet libc, uClibc; gcc, Intel C Sun Solaris 2.6 and above; Sun C, gcc Open UNIX 8.0.0 FreeBSD 4.9 and above HP HP-UX B.11.11, B.11.23; HP C/ANSI C, gcc HP Tru64 UNIX 4.0G, 5.1B; Developers' Toolkit C, gcc NetBSD 1.6, 2.0 IBM AIX 5.1; VisualAge C, gcc Cray UNICOS 9.0.2.2 Control Data EP/IX 2.2.1AA; /svr4/bin/cc OpenBSD 3.3 Apple Darwin 6.8 Apple Mac OS X 10.2 Server NEC UX/4800 Release11.5 Rev.A NEC SUPER-UX 10.2 DragonFlyBSD 1.3.7-DEVELOPMENT If your system does not appear in this list, just try it out. Whether it works or not, you should contact the development list and report the results. But note that I strongly discourage from porting mailx to Windows and environments that make Windows look Unix-like; I won't accept any patches or suggestions that go in this direction. There are two major reasons for this: First, any port makes maintaining harder; there are always more work-arounds in the source, and introducing new features involves the question whether they will work an all supported platforms. The more different a platform behaves from, let's say, the common Unix way, the more hacks have to be made, costing human time that could otherwise have been used to enhance the software for Unix platforms. Windows is just not worth this, and here we are at the second point: Porting software to Windows encourages people to use -- that is: to buy -- Windows. It supports a company that is known to threaten Open Source software like mailx. In short, porting mailx (or similar free software) to Windows has an ill effect on that software. Don't do it. Note that my statement doesn't legally restrict you if you want to port mailx to any platform. This would not be the way of free software either, especially since I might be wrong in the future; as an example, porting free software to mainframes of a certain company is considered a good thing today. I just wish to express my opinion as a free software developer, and to inform you that I don't maintain such a port. Mailbox formats =============== Mailx supports the mbox and maildir mailbox formats. The mbox format variant based on the 'Content-Length:' header field that is used on most SVr4 systems by default is not supported by mailx. As this format generally is a design flaw, you should fix your system by either using procmail for local mail delivery, which is a good idea anyway, or at least add the -E flag to the Mlocal line in /etc/sendmail.cf if using /usr/lib/mail.local. Although it is not bad, just obsolete, similar considerations apply to the MMDF format used on OpenServer systems; unless you switch to procmail (or contribute support for this format), mailx will not be able to read your mailbox there. Questions, suggestions, bug reports =================================== Please use the 'nail-devel' mailing list for questions, suggestions, or bug reports. This has at least three advantages over contacting me directly: 1. Other people can comment on the issue. They might have solved a similar problem, or might be willing to implement improvements. 2. Since all posts are archived, a problem needs to be commented once only, and the answers are readily available on the web then. 3. Unless you had an acceptable reason to contact me directly, I will refuse to give you technical answers by personal mail. Thus if you ignore this advice, you will just have to resend your message to the list. Also before you send something to the list, make sure that you did the following: 1. Check out that you are using a binary made from pristine sources of the latest release. This is particularly important if you received your mailx binaries from a third-party vendor. If you are unwilling to do this for whatever reason, use the support channels of your vendor and avoid abusing the Open Source development model. 2. Check that your issue is not already solved or commented in the existing documentation. This does not only involve reading this file; you also need to look at the manual page and the ChangeLog. After doing that, you need to search the mailing list archive for related topics. Remember that you are spending other people's spare time when you ask questions, and that you just waste it if your question was a superfluous one. 3. If you are reporting a bug, try to reproduce it and include detailed instructions for doing that in your report. If you cannot reproduce the bug, document carefully what you have done before the problem occurred. The more information you provide, the greater are the chances that the bug can be fixed quickly. Both the contact instructions and the list archive are available at . You need to subscribe in order to post to the list. Enjoy! Gunnar Ritter Freiburg i. Br. Germany 01/03/07 heirloom-mailx-12.5/TODO000066400000000000000000000057531155563371200151010ustar00rootroot00000000000000Before you start making additions to mailx, subscribe to the development mailing list at to coordinate your efforts with the maintainer and other people. ------------------------------------------------------------------------ The following features are missing for conformance with the Single Unix Specification, v.3: * LC_TIME environment. This conflicts with the mail RFCs and historical usage. It was optional POSIX.2, but the newer standards mention this in the rationale only. Won't be implemented here. * onehop variable (not demanded by POSIX standards). Seems absolutely obsolete. If anybody has a need for it, he should contribute code. ------------------------------------------------------------------------ The following IMAP features could be implemented in the future. If you need them and cannot wait, contribute code. - The MIME capabilities of IMAP could be used so that not entire messages are downloaded all the time, but just the parts that are actually needed. - SASL authentication methods could be implemented. - The deletion code (more general, all STORE code) should be able to generate requests for ranges of messages to speed up operation. - Large messages should be transferred in separate parts with IMAP so that the operation can be interrupted in between. ------------------------------------------------------------------------ The POP3 client supports USER/PASS and APOP authentications only. If you need other authentication methods and have the ability to test them, please write and contribute code. A variable should be added to specify the default character set for messages without MIME declarations. If you want to edit arbitrary header fields with the 'editheaders' option, you are invited to supply code for it. Be warned that this is a nontrivial task due to MIME. Contact the development mailing list before you start coding. Several people have suggested adding support for arrow keys at the command prompt, autocompletion for attachment filenames, etc. I am personally not going to implement this as X Window Copy-and-Paste satisfies me. But if you desparately need it, you're invited to contribute code. You can't use libreadline, though, because its license is incompatible, and I insist that your implementation is done in a way that a) mailx can still be compiled without requiring any further libraries (i. e. your code is optional) and b) your code works for all kinds of terminals, not just VT100/ANSI ones. Mailx will not support 'Return-Receipt-To' or 'Disposition-Notification-To' header fields, neither for the sending nor for the receiving part. For the sending part it plainly doesn't work because one wants to know whether the human recipient has read the mail and not if he clicked on some send-a-receipt-button while looking out of the window. For the receiving part, it is simply annoying. If you want a confirmation, ask the recipient to send one in the body of the message. Gunnar Ritter 3/4/06 heirloom-mailx-12.5/aux.c000066400000000000000000000616131155563371200153470ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)aux.c 2.83 (gritter) 3/4/06"; #endif #endif /* not lint */ #include "rcv.h" #include "extern.h" #include #include #include #include #include #ifdef HAVE_WCTYPE_H #include #endif /* HAVE_WCTYPE_H */ #ifdef HAVE_WCWIDTH #include #endif /* HAVE_WCWIDTH */ #include #include #include #include #include #include #include #include "md5.h" /* * Mail -- a mail program * * Auxiliary functions. */ /* * Return a pointer to a dynamic copy of the argument. */ char * savestr(const char *str) { char *new; int size = strlen(str) + 1; if ((new = salloc(size)) != NULL) memcpy(new, str, size); return new; } /* * Make a copy of new argument incorporating old one. */ char * save2str(const char *str, const char *old) { char *new; int newsize = strlen(str) + 1; int oldsize = old ? strlen(old) + 1 : 0; if ((new = salloc(newsize + oldsize)) != NULL) { if (oldsize) { memcpy(new, old, oldsize); new[oldsize - 1] = ' '; } memcpy(new + oldsize, str, newsize); } return new; } char * savecat(const char *s1, const char *s2) { const char *cp; char *ns, *np; np = ns = salloc(strlen(s1) + strlen(s2) + 1); for (cp = s1; *cp; cp++) *np++ = *cp; for (cp = s2; *cp; cp++) *np++ = *cp; *np = '\0'; return ns; } #include #ifndef HAVE_SNPRINTF /* * Lazy vsprintf wrapper. */ int snprintf(char *str, size_t size, const char *format, ...) { va_list ap; int ret; va_start(ap, format); ret = vsprintf(str, format, ap); va_end(ap); return ret; } #endif /* !HAVE_SNPRINTF */ /* * Announce a fatal error and die. */ void panic(const char *format, ...) { va_list ap; va_start(ap, format); fprintf(stderr, catgets(catd, CATSET, 1, "panic: ")); vfprintf(stderr, format, ap); va_end(ap); fprintf(stderr, catgets(catd, CATSET, 2, "\n")); fflush(stderr); abort(); } void holdint(void) { sigset_t set; sigemptyset(&set); sigaddset(&set, SIGINT); sigprocmask(SIG_BLOCK, &set, NULL); } void relseint(void) { sigset_t set; sigemptyset(&set); sigaddset(&set, SIGINT); sigprocmask(SIG_UNBLOCK, &set, NULL); } /* * Touch the named message by setting its MTOUCH flag. * Touched messages have the effect of not being sent * back to the system mailbox on exit. */ void touch(struct message *mp) { mp->m_flag |= MTOUCH; if ((mp->m_flag & MREAD) == 0) mp->m_flag |= MREAD|MSTATUS; } /* * Test to see if the passed file name is a directory. * Return true if it is. */ int is_dir(char *name) { struct stat sbuf; if (stat(name, &sbuf) < 0) return(0); return(S_ISDIR(sbuf.st_mode)); } /* * Count the number of arguments in the given string raw list. */ int argcount(char **argv) { char **ap; for (ap = argv; *ap++ != NULL;) ; return ap - argv - 1; } /* * Copy a string, lowercasing it as we go. */ void i_strcpy(char *dest, const char *src, int size) { char *max; max=dest+size-1; while (dest<=max) { *dest++ = lowerconv(*src & 0377); if (*src++ == '\0') break; } } char * i_strdup(const char *src) { int sz; char *dest; sz = strlen(src) + 1; dest = salloc(sz); i_strcpy(dest, src, sz); return dest; } /* * Convert a string to lowercase, in-place and with multibyte-aware. */ void makelow(char *cp) { #if defined (HAVE_MBTOWC) && defined (HAVE_WCTYPE_H) if (mb_cur_max > 1) { char *tp = cp; wchar_t wc; int len; while (*cp) { len = mbtowc(&wc, cp, mb_cur_max); if (len < 0) *tp++ = *cp++; else { wc = towlower(wc); if (wctomb(tp, wc) == len) tp += len, cp += len; else *tp++ = *cp++; } } } else #endif /* HAVE_MBTOWC && HAVE_WCTYPE_H */ { do *cp = tolower(*cp & 0377); while (*cp++); } } int substr(const char *str, const char *sub) { const char *cp, *backup; cp = sub; backup = str; while (*str && *cp) { #if defined (HAVE_MBTOWC) && defined (HAVE_WCTYPE_H) if (mb_cur_max > 1) { wchar_t c, c2; int sz; if ((sz = mbtowc(&c, cp, mb_cur_max)) < 0) goto singlebyte; cp += sz; if ((sz = mbtowc(&c2, str, mb_cur_max)) < 0) goto singlebyte; str += sz; c = towupper(c); c2 = towupper(c2); if (c != c2) { if ((sz = mbtowc(&c, backup, mb_cur_max)) > 0) { backup += sz; str = backup; } else str = ++backup; cp = sub; } } else #endif /* HAVE_MBTOWC && HAVE_WCTYPE_H */ { int c, c2; #if defined (HAVE_MBTOWC) && defined (HAVE_WCTYPE_H) singlebyte: #endif /* HAVE_MBTOWC && HAVE_WCTYPE_H */ c = *cp++ & 0377; if (islower(c)) c = toupper(c); c2 = *str++ & 0377; if (islower(c2)) c2 = toupper(c2); if (c != c2) { str = ++backup; cp = sub; } } } return *cp == '\0'; } char * colalign(const char *cp, int col, int fill) { int n, sz; char *nb, *np; np = nb = salloc(mb_cur_max * strlen(cp) + col + 1); while (*cp) { #if defined (HAVE_MBTOWC) && defined (HAVE_WCWIDTH) if (mb_cur_max > 1) { wchar_t wc; if ((sz = mbtowc(&wc, cp, mb_cur_max)) < 0) { n = sz = 1; } else { if ((n = wcwidth(wc)) < 0) n = 1; } } else #endif /* HAVE_MBTOWC && HAVE_WCWIDTH */ { n = sz = 1; } if (n > col) break; col -= n; if (sz == 1 && spacechar(*cp&0377)) { *np++ = ' '; cp++; } else while (sz--) *np++ = *cp++; } if (fill) while (col-- > 0) *np++ = ' '; *np = '\0'; return nb; } void try_pager(FILE *fp) { long lines = 0; int c; char *cp; fflush(fp); rewind(fp); while ((c = getc(fp)) != EOF) if (c == '\n') lines++; rewind(fp); if (is_a_tty[0] && is_a_tty[1] && (cp = value("crt")) != NULL && lines > (*cp ? atol(cp) : scrnheight)) run_command(get_pager(), 0, fileno(fp), -1, NULL, NULL, NULL); else while ((c = getc(fp)) != EOF) putchar(c); } /* * The following code deals with input stacking to do source * commands. All but the current file pointer are saved on * the stack. */ static int ssp; /* Top of file stack */ struct sstack { FILE *s_file; /* File we were in. */ enum condition s_cond; /* Saved state of conditionals */ int s_loading; /* Loading .mailrc, etc. */ #define SSTACK 20 } sstack[SSTACK]; /* * Pushdown current input file and switch to a new one. * Set the global flag "sourcing" so that others will realize * that they are no longer reading from a tty (in all probability). */ int source(void *v) { char **arglist = v; FILE *fi; char *cp; if ((cp = expand(*arglist)) == NULL) return(1); if ((fi = Fopen(cp, "r")) == NULL) { perror(cp); return(1); } if (ssp >= SSTACK - 1) { printf(catgets(catd, CATSET, 3, "Too much \"sourcing\" going on.\n")); Fclose(fi); return(1); } sstack[ssp].s_file = input; sstack[ssp].s_cond = cond; sstack[ssp].s_loading = loading; ssp++; loading = 0; cond = CANY; input = fi; sourcing++; return(0); } /* * Pop the current input back to the previous level. * Update the "sourcing" flag as appropriate. */ int unstack(void) { if (ssp <= 0) { printf(catgets(catd, CATSET, 4, "\"Source\" stack over-pop.\n")); sourcing = 0; return(1); } Fclose(input); if (cond != CANY) printf(catgets(catd, CATSET, 5, "Unmatched \"if\"\n")); ssp--; cond = sstack[ssp].s_cond; loading = sstack[ssp].s_loading; input = sstack[ssp].s_file; if (ssp == 0) sourcing = loading; return(0); } /* * Touch the indicated file. * This is nifty for the shell. */ void alter(char *name) { struct stat sb; struct utimbuf utb; if (stat(name, &sb)) return; utb.actime = time((time_t *)0) + 1; utb.modtime = sb.st_mtime; utime(name, &utb); } /* * Examine the passed line buffer and * return true if it is all blanks and tabs. */ int blankline(char *linebuf) { char *cp; for (cp = linebuf; *cp; cp++) if (!blankchar(*cp & 0377)) return(0); return(1); } /* * Are any of the characters in the two strings the same? */ int anyof(char *s1, char *s2) { while (*s1) if (strchr(s2, *s1++)) return 1; return 0; } /* * Determine if as1 is a valid prefix of as2. * Return true if yep. */ int is_prefix(const char *as1, const char *as2) { const char *s1, *s2; s1 = as1; s2 = as2; while (*s1++ == *s2) if (*s2++ == '\0') return(1); return(*--s1 == '\0'); } char * last_at_before_slash(const char *sp) { const char *cp; for (cp = sp; *cp; cp++) if (*cp == '/') break; while (cp > sp && *--cp != '@'); return *cp == '@' ? (char *)cp : NULL; } enum protocol which_protocol(const char *name) { register const char *cp; char *np; size_t sz; struct stat st; enum protocol p; if (name[0] == '%' && name[1] == ':') name += 2; for (cp = name; *cp && *cp != ':'; cp++) if (!alnumchar(*cp&0377)) goto file; if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') { if (strncmp(name, "pop3://", 7) == 0) return PROTO_POP3; if (strncmp(name, "pop3s://", 8) == 0) #ifdef USE_SSL return PROTO_POP3; #else /* !USE_SSL */ fprintf(stderr, catgets(catd, CATSET, 225, "No SSL support compiled in.\n")); #endif /* !USE_SSL */ if (strncmp(name, "imap://", 7) == 0) return PROTO_IMAP; if (strncmp(name, "imaps://", 8) == 0) #ifdef USE_SSL return PROTO_IMAP; #else /* !USE_SSL */ fprintf(stderr, catgets(catd, CATSET, 225, "No SSL support compiled in.\n")); #endif /* !USE_SSL */ return PROTO_UNKNOWN; } else { file: p = PROTO_FILE; np = ac_alloc((sz = strlen(name)) + 5); strcpy(np, name); if (stat(name, &st) == 0) { if (S_ISDIR(st.st_mode)) { strcpy(&np[sz], "/tmp"); if (stat(np, &st) == 0 && S_ISDIR(st.st_mode)) { strcpy(&np[sz], "/new"); if (stat(np, &st) == 0 && S_ISDIR(st.st_mode)) { strcpy(&np[sz], "/cur"); if (stat(np, &st) == 0 && S_ISDIR(st.st_mode)) p = PROTO_MAILDIR; } } } } else { strcpy(&np[sz], ".gz"); if (stat(np, &st) < 0) { strcpy(&np[sz], ".bz2"); if (stat(np, &st) < 0) { if ((cp = value("newfolders")) != 0 && strcmp(cp, "maildir") == 0) p = PROTO_MAILDIR; } } } ac_free(np); return p; } } const char * protfile(const char *xcp) { const char *cp = xcp; int state = 0; while (*cp) { if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') { cp += 3; state = 1; } if (cp[0] == '/' && state == 1) return &cp[1]; if (cp[0] == '/') return xcp; cp++; } return cp; } char * protbase(const char *cp) { char *n = salloc(strlen(cp) + 1); char *np = n; while (*cp) { if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') { *np++ = *cp++; *np++ = *cp++; *np++ = *cp++; } else if (cp[0] == '/') break; else *np++ = *cp++; } *np = '\0'; return n; } int disconnected(const char *file) { char *cp, *cq, *vp; int vs, r; if (value("disconnected")) return 1; cp = protbase(file); if (strncmp(cp, "imap://", 7) == 0) cp += 7; else if (strncmp(cp, "imaps://", 8) == 0) cp += 8; else return 0; if ((cq = strchr(cp, ':')) != NULL) *cq = '\0'; vp = ac_alloc(vs = strlen(cp) + 14); snprintf(vp, vs, "disconnected-%s", cp); r = value(vp) != NULL; ac_free(vp); return r; } unsigned pjw(const char *cp) { unsigned h = 0, g; cp--; while (*++cp) { h = (h << 4 & 0xffffffff) + (*cp&0377); if ((g = h & 0xf0000000) != 0) { h = h ^ g >> 24; h = h ^ g; } } return h; } long nextprime(long n) { const long primes[] = { 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521, 131071, 262139, 524287, 1048573, 2097143, 4194301, 8388593, 16777213, 33554393, 67108859, 134217689, 268435399, 536870909, 1073741789, 2147483647 }; long mprime = 7; int i; for (i = 0; i < sizeof primes / sizeof *primes; i++) if ((mprime = primes[i]) >= (n < 65536 ? n*4 : n < 262144 ? n*2 : n)) break; if (i == sizeof primes / sizeof *primes) mprime = n; /* not so prime, but better than failure */ return mprime; } #define Hexchar(n) ((n)>9 ? (n)-10+'A' : (n)+'0') #define hexchar(n) ((n)>9 ? (n)-10+'a' : (n)+'0') char * strenc(const char *cp) { char *n, *np; np = n = salloc(strlen(cp) * 3 + 1); while (*cp) { if (alnumchar(*cp&0377) || *cp == '_' || *cp == '@' || (np > n && (*cp == '.' || *cp == '-' || *cp == ':'))) *np++ = *cp; else { *np++ = '%'; *np++ = Hexchar((*cp&0xf0) >> 4); *np++ = Hexchar(*cp&0x0f); } cp++; } *np = '\0'; return n; } char * strdec(const char *cp) { char *n, *np; np = n = salloc(strlen(cp) + 1); while (*cp) { if (cp[0] == '%' && cp[1] && cp[2]) { *np = (int)(cp[1]>'9'?cp[1]-'A'+10:cp[1]-'0') << 4; *np++ |= cp[2]>'9'?cp[2]-'A'+10:cp[2]-'0'; cp += 3; } else *np++ = *cp++; } *np = '\0'; return n; } char * md5tohex(const void *vp) { char *hex; const char *cp = vp; int i; hex = salloc(33); for (i = 0; i < 16; i++) { hex[2*i] = hexchar((cp[i]&0xf0) >> 4); hex[2*i+1] = hexchar(cp[i]&0x0f); } hex[32] = '\0'; return hex; } char * cram_md5_string(const char *user, const char *pass, const char *b64) { struct str in, out; char digest[16], *cp, *sp, *rp, *xp; int ss, rs; in.s = (char *)b64; in.l = strlen(in.s); mime_fromb64(&in, &out, 0); hmac_md5((unsigned char *)out.s, out.l, (unsigned char *)pass, strlen(pass), digest); free(out.s); xp = md5tohex(digest); sp = ac_alloc(ss = strlen(user) + strlen(xp) + 2); snprintf(sp, ss, "%s %s", user, xp); cp = strtob64(sp); ac_free(sp); rp = salloc(rs = strlen(cp) + 3); snprintf(rp, rs, "%s\r\n", cp); free(cp); return rp; } char * getuser(void) { char *line = NULL, *user; size_t linesize = 0; if (is_a_tty[0]) { fputs("User: ", stdout); fflush(stdout); } if (readline(stdin, &line, &linesize) == 0) { if (line) free(line); return NULL; } user = savestr(line); free(line); return user; } char * getpassword(struct termios *otio, int *reset_tio, const char *query) { struct termios tio; char *line = NULL, *pass; size_t linesize = 0; int i; if (is_a_tty[0]) { fputs(query ? query : "Password:", stdout); fflush(stdout); tcgetattr(0, &tio); *otio = tio; tio.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); *reset_tio = 1; tcsetattr(0, TCSAFLUSH, &tio); } i = readline(stdin, &line, &linesize); if (is_a_tty[0]) { fputc('\n', stdout); tcsetattr(0, TCSADRAIN, otio); } *reset_tio = 0; if (i < 0) { if (line) free(line); return NULL; } pass = savestr(line); free(line); return pass; } void transflags(struct message *omessage, long omsgCount, int transparent) { struct message *omp, *nmp, *newdot, *newprevdot; int hf; omp = omessage; nmp = message; newdot = message; newprevdot = NULL; while (omp < &omessage[omsgCount] && nmp < &message[msgCount]) { if (dot && nmp->m_uid == dot->m_uid) newdot = nmp; if (prevdot && nmp->m_uid == prevdot->m_uid) newprevdot = nmp; if (omp->m_uid == nmp->m_uid) { hf = nmp->m_flag & MHIDDEN; if (transparent && mb.mb_type == MB_IMAP) omp->m_flag &= ~MHIDDEN; *nmp++ = *omp++; if (transparent && mb.mb_type == MB_CACHE) nmp[-1].m_flag |= hf; } else if (omp->m_uid < nmp->m_uid) omp++; else nmp++; } dot = newdot; setdot(newdot); prevdot = newprevdot; free(omessage); } char * getrandstring(size_t length) { static unsigned char nodedigest[16]; static pid_t pid; int i, fd = -1; char *data; char *cp, *rp; MD5_CTX ctx; data = salloc(length); if ((fd = open("/dev/urandom", O_RDONLY)) < 0 || read(fd, data, length) != length) { if (pid == 0) { pid = getpid(); srand(pid); cp = nodename(0); MD5Init(&ctx); MD5Update(&ctx, (unsigned char *)cp, strlen(cp)); MD5Final(nodedigest, &ctx); } for (i = 0; i < length; i++) data[i] = (int)(255 * (rand() / (RAND_MAX + 1.0))) ^ nodedigest[i % sizeof nodedigest]; } if (fd > 0) close(fd); cp = memtob64(data, length); rp = salloc(length+1); strncpy(rp, cp, length)[length] = '\0'; free(cp); return rp; } void out_of_memory(void) { panic("no memory"); } void * smalloc(size_t s) { void *p; if (s == 0) s = 1; if ((p = malloc(s)) == NULL) out_of_memory(); return p; } void * srealloc(void *v, size_t s) { void *r; if (s == 0) s = 1; if (v == NULL) return smalloc(s); if ((r = realloc(v, s)) == NULL) out_of_memory(); return r; } void * scalloc(size_t nmemb, size_t size) { void *vp; if (size == 0) size = 1; if ((vp = calloc(nmemb, size)) == NULL) out_of_memory(); return vp; } char * sstpcpy(char *dst, const char *src) { while ((*dst = *src++) != '\0') dst++; return dst; } char * sstrdup(const char *cp) { char *dp; if (cp) { dp = smalloc(strlen(cp) + 1); strcpy(dp, cp); return dp; } else return NULL; } enum okay makedir(const char *name) { int e; struct stat st; if (mkdir(name, 0700) < 0) { e = errno; if ((e == EEXIST || e == ENOSYS) && stat(name, &st) == 0 && (st.st_mode&S_IFMT) == S_IFDIR) return OKAY; return STOP; } return OKAY; } #ifdef HAVE_FCHDIR enum okay cwget(struct cw *cw) { if ((cw->cw_fd = open(".", O_RDONLY)) < 0) return STOP; if (fchdir(cw->cw_fd) < 0) { close(cw->cw_fd); return STOP; } return OKAY; } enum okay cwret(struct cw *cw) { if (fchdir(cw->cw_fd) < 0) return STOP; return OKAY; } void cwrelse(struct cw *cw) { close(cw->cw_fd); } #else /* !HAVE_FCHDIR */ enum okay cwget(struct cw *cw) { if (getcwd(cw->cw_wd, sizeof cw->cw_wd) == NULL || chdir(cw->cw_wd) < 0) return STOP; return OKAY; } enum okay cwret(struct cw *cw) { if (chdir(cw->cw_wd) < 0) return STOP; return OKAY; } /*ARGSUSED*/ void cwrelse(struct cw *cw) { } #endif /* !HAVE_FCHDIR */ void makeprint(struct str *in, struct str *out) { static int print_all_chars = -1; char *inp, *outp; size_t msz, dist; out->s = smalloc(msz = in->l + 1); if (print_all_chars == -1) print_all_chars = value("print-all-chars") != NULL; if (print_all_chars) { memcpy(out->s, in->s, in->l); out->l = in->l; out->s[out->l] = '\0'; return; } inp = in->s; outp = out->s; #if defined (HAVE_MBTOWC) && defined (HAVE_WCTYPE_H) if (mb_cur_max > 1) { wchar_t wc; char mb[MB_LEN_MAX+1]; int i, n; out->l = 0; while (inp < &in->s[in->l]) { if (*inp & 0200) n = mbtowc(&wc, inp, &in->s[in->l] - inp); else { wc = *inp; n = 1; } if (n < 0) { mbtowc(&wc, NULL, mb_cur_max); wc = utf8 ? 0xFFFD : '?'; n = 1; } else if (n == 0) n = 1; inp += n; if (!iswprint(wc) && wc != '\n' && wc != '\r' && wc != '\b' && wc != '\t') { if ((wc & ~(wchar_t)037) == 0) wc = utf8 ? 0x2400 | wc : '?'; else if (wc == 0177) wc = utf8 ? 0x2421 : '?'; else wc = utf8 ? 0x2426 : '?'; } if ((n = wctomb(mb, wc)) <= 0) continue; out->l += n; if (out->l >= msz - 1) { dist = outp - out->s; out->s = srealloc(out->s, msz += 32); outp = &out->s[dist]; } for (i = 0; i < n; i++) *outp++ = mb[i]; } } else #endif /* HAVE_MBTOWC && HAVE_WCTYPE_H */ { int c; while (inp < &in->s[in->l]) { c = *inp++ & 0377; if (!isprint(c) && c != '\n' && c != '\r' && c != '\b' && c != '\t') c = '?'; *outp++ = c; } out->l = in->l; } out->s[out->l] = '\0'; } char * prstr(const char *s) { struct str in, out; char *rp; in.s = (char *)s; in.l = strlen(s); makeprint(&in, &out); rp = salloc(out.l + 1); memcpy(rp, out.s, out.l); rp[out.l] = '\0'; free(out.s); return rp; } int prout(const char *s, size_t sz, FILE *fp) { struct str in, out; int n; in.s = (char *)s; in.l = sz; makeprint(&in, &out); n = fwrite(out.s, 1, out.l, fp); free(out.s); return n; } /* * Print out a Unicode character or a substitute for it. */ int putuc(int u, int c, FILE *fp) { #if defined (HAVE_MBTOWC) && defined (HAVE_WCTYPE_H) if (utf8 && u & ~(wchar_t)0177) { char mb[MB_LEN_MAX]; int i, n, r = 0; if ((n = wctomb(mb, u)) > 0) { for (i = 0; i < n; i++) r += putc(mb[i] & 0377, fp) != EOF; return r; } else if (n == 0) return putc('\0', fp) != EOF; else return 0; } else #endif /* HAVE_MBTOWC && HAVE_WCTYPE_H */ return putc(c, fp) != EOF; } /* * Locale-independent character class functions. */ int asccasecmp(const char *s1, const char *s2) { register int cmp; do if ((cmp = lowerconv(*s1 & 0377) - lowerconv(*s2 & 0377)) != 0) return cmp; while (*s1++ != '\0' && *s2++ != '\0'); return 0; } int ascncasecmp(const char *s1, const char *s2, size_t sz) { register int cmp; size_t i = 1; if (sz == 0) return 0; do if ((cmp = lowerconv(*s1 & 0377) - lowerconv(*s2 & 0377)) != 0) return cmp; while (i++ < sz && *s1++ != '\0' && *s2++ != '\0'); return 0; } char * asccasestr(const char *haystack, const char *xneedle) { char *needle, *NEEDLE; int i, sz; sz = strlen(xneedle); if (sz == 0) return (char *)haystack; needle = ac_alloc(sz); NEEDLE = ac_alloc(sz); for (i = 0; i < sz; i++) { needle[i] = lowerconv(xneedle[i]&0377); NEEDLE[i] = upperconv(xneedle[i]&0377); } while (*haystack) { if (*haystack == *needle || *haystack == *NEEDLE) { for (i = 1; i < sz; i++) if (haystack[i] != needle[i] && haystack[i] != NEEDLE[i]) break; if (i == sz) return (char *)haystack; } haystack++; } return NULL; } const unsigned char class_char[] = { /* 000 nul 001 soh 002 stx 003 etx 004 eot 005 enq 006 ack 007 bel */ C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL, /* 010 bs 011 ht 012 nl 013 vt 014 np 015 cr 016 so 017 si */ C_CNTRL,C_BLANK,C_WHITE,C_SPACE,C_SPACE,C_SPACE,C_CNTRL,C_CNTRL, /* 020 dle 021 dc1 022 dc2 023 dc3 024 dc4 025 nak 026 syn 027 etb */ C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL, /* 030 can 031 em 032 sub 033 esc 034 fs 035 gs 036 rs 037 us */ C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL, /* 040 sp 041 ! 042 " 043 # 044 $ 045 % 046 & 047 ' */ C_BLANK,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT, /* 050 ( 051 ) 052 * 053 + 054 , 055 - 056 . 057 / */ C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT, /* 060 0 061 1 062 2 063 3 064 4 065 5 066 6 067 7 */ C_OCTAL,C_OCTAL,C_OCTAL,C_OCTAL,C_OCTAL,C_OCTAL,C_OCTAL,C_OCTAL, /* 070 8 071 9 072 : 073 ; 074 < 075 = 076 > 077 ? */ C_DIGIT,C_DIGIT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT, /* 100 @ 101 A 102 B 103 C 104 D 105 E 106 F 107 G */ C_PUNCT,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER, /* 110 H 111 I 112 J 113 K 114 L 115 M 116 N 117 O */ C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER, /* 120 P 121 Q 122 R 123 S 124 T 125 U 126 V 127 W */ C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER, /* 130 X 131 Y 132 Z 133 [ 134 \ 135 ] 136 ^ 137 _ */ C_UPPER,C_UPPER,C_UPPER,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT, /* 140 ` 141 a 142 b 143 c 144 d 145 e 146 f 147 g */ C_PUNCT,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER, /* 150 h 151 i 152 j 153 k 154 l 155 m 156 n 157 o */ C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER, /* 160 p 161 q 162 r 163 s 164 t 165 u 166 v 167 w */ C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER, /* 170 x 171 y 172 z 173 { 174 | 175 } 176 ~ 177 del */ C_LOWER,C_LOWER,C_LOWER,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_CNTRL }; heirloom-mailx-12.5/base64.c000066400000000000000000000133101155563371200156250ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * These base64 routines are derived from the metamail-2.7 sources which * state the following copyright notice: * * Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore) * * Permission to use, copy, modify, and distribute this material * for any purpose and without fee is hereby granted, provided * that the above copyright notice and this permission notice * appear in all copies, and that the name of Bellcore not be * used in advertising or publicity pertaining to this * material without the specific, prior written permission * of an authorized representative of Bellcore. BELLCORE * MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY * OF THIS MATERIAL FOR ANY PURPOSE. IT IS PROVIDED "AS IS", * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)base64.c 2.14 (gritter) 4/21/06"; #endif #endif /* not lint */ /* * Mail -- a mail program * * base64 functions */ #include "rcv.h" #include "extern.h" static const char b64table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static const signed char b64index[] = { -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 }; #define char64(c) ((c) < 0 ? -1 : b64index[(int)(c)]) static signed char *ctob64(unsigned char *p, int pad); /* * Convert three characters to base64. */ static signed char * ctob64(unsigned char *p, int pad) { static signed char b64[4]; b64[0] = b64table[p[0] >> 2]; b64[1] = b64table[((p[0] & 0x3) << 4) | ((p[1] & 0xF0) >> 4)]; if (pad == 2) { b64[1] = b64table[(p[0] & 0x3) << 4]; b64[2] = b64[3] = '='; } else if (pad == 1) { b64[2] = b64table[((p[1] & 0xF) << 2)]; b64[3] = '='; } else { b64[2] = b64table[((p[1] & 0xF) << 2) | ((p[2] & 0xC0) >> 6)]; b64[3] = b64table[p[2] & 0x3F]; } return b64; } char * strtob64(const char *p) { return memtob64(p, strlen(p)); } char * memtob64(const void *vp, size_t isz) { char q[3]; const char *p = vp; signed char *h; size_t c = 0; int i, l = 0, sz = 0, pads; char *rs = NULL; if (isz == 0) { rs = smalloc(1); *rs = '\0'; return rs; } do { for (pads = 2, i = 0; i <= 2; i++, pads--) { q[i] = p[c++]; if (c == isz) break; } h = ctob64((unsigned char *)q, pads); if (l + 5 >= sz) rs = srealloc(rs, sz = l + 100); for (i = 0; i < 4; i++) rs[l++] = h[i]; } while (c < isz); rs[l] = '\0'; return rs; } /* * Write to a file converting to base64. The input must be aligned * e.g. at 972 character bounds. */ size_t mime_write_tob64(struct str *in, FILE *fo, int is_header) { char *p, *upper, q[3]; signed char *h; int i, l, pads; size_t sz; sz = 0; upper = in->s + in->l; for (p = in->s, l = 0; p < upper; ) { for (pads = 2, i = 0; i <= 2; i++, pads--) { q[i] = *p++; if (p == upper) break; } h = ctob64((unsigned char *)q, pads); fwrite(h, sizeof(char), 4, fo); sz += 4, l += 4; if (l >= 71) { putc('\n', fo), sz++; l = 0; } } if (l != 0 && !is_header) { putc('\n', fo), sz++; } return sz; } /* * Decode from base64. */ void mime_fromb64(struct str *in, struct str *out, int is_text) { char *p, *q, *upper; signed char c, d, e, f, g; int done = 0, newline = 0; out->s = smalloc(in->l * 3 / 4 + 2); out->l = 0; upper = in->s + in->l; for (p = in->s, q = out->s; p < upper; ) { while (c = *p++, whitechar(c)); if (p >= upper) break; if (done) continue; while (d = *p++, whitechar(d)); if (p >= upper) break; while (e = *p++, whitechar(e)); if (p >= upper) break; while (f = *p++, whitechar(f)); if (c == '=' || d == '=') { done = 1; continue; } c = char64(c); d = char64(d); g = ((c << 2) | ((d & 0x30) >> 4)); if (is_text) { if (g == '\r') { newline = 1; } else if (g == '\n' && newline) { q--; out->l--; newline = 0; } else { newline = 0; } } *q++ = g; out->l++; if (e == '=') { done = 1; } else { e = char64(e); g = (((d & 0xF) << 4) | ((e & 0x3C) >> 2)); if (is_text) { if (g == '\r') { newline = 1; } else if (g == '\n' && newline) { q--; out->l--; newline = 0; } else { newline = 0; } } *q++ = g; out->l++; if (f == '=') { done = 1; } else { f = char64(f); g = (((e & 0x03) << 6) | f); if (is_text) { if (g == '\r') { newline = 1; } else if (g == '\n' && newline) { q--; out->l--; newline = 0; } else { newline = 0; } } *q++ = g; out->l++; } } } return; } /* * Buffer the base64 input so mime_fromb64 gets always multiples of * 4 characters. * As we have only one buffer, this function is not reentrant. */ void mime_fromb64_b(struct str *in, struct str *out, int is_text, FILE *f) { static signed char b[4]; static int n; static FILE *f_b = (FILE *)-1; signed char c; int i; struct str nin; nin.s = smalloc(in->l + n); if (n != 0 && f_b == f) { for (nin.l = 0; nin.l < n; nin.l++) nin.s[nin.l] = b[nin.l]; } else { nin.l = 0; n = 0; } for (i = 0; i <= in->l; i++) { c = in->s[i]; if (char64(c) == -1 && c != '=') continue; b[n] = nin.s[nin.l++] = c; if (n >= 3) n = 0; else n++; } nin.l -= n; mime_fromb64(&nin, out, is_text); free(nin.s); f_b = f; } heirloom-mailx-12.5/cache.c000066400000000000000000000475341155563371200156230ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 2004 * Gunnar Ritter. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Gunnar Ritter * and his contributors. * 4. Neither the name of Gunnar Ritter nor the names of his contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)cache.c 1.61 (gritter) 3/4/06"; #endif #endif /* not lint */ #include "config.h" #ifdef HAVE_SOCKETS #include "rcv.h" #include "extern.h" #include #include #include #include #include #include #include /* * Mail -- a mail program * * A cache for IMAP. */ static char *encname(struct mailbox *mp, const char *name, int same, const char *box); static char *encuid(struct mailbox *mp, unsigned long uid); static FILE *clean(struct mailbox *mp, struct cw *cw); static unsigned long *builds(long *contentelem); static void purge(struct mailbox *mp, struct message *m, long mc, struct cw *cw, const char *name); static int longlt(const void *a, const void *b); static void remve(unsigned long n); static FILE *cache_queue1(struct mailbox *mp, char *mode, char **xname); static enum okay dequeue1(struct mailbox *mp); static const char infofmt[] = "%c %lu %u %lu %lu"; #define INITSKIP 128L #define USEBITS(f) \ ((f) & (MSAVED|MDELETED|MREAD|MBOXED|MNEW|MFLAGGED|MANSWERED|MDRAFTED)) static const char README1[] = "\ This is a cache directory maintained by mailx(1). You should not change any\n\ files within. Nevertheless, the structure is as follows: Each subdirectory\n\ of the current directory represents an IMAP account, and each subdirectory\n\ below that represents a mailbox. Each mailbox directory contains a file\n\ named UIDVALIDITY which describes the validity in relation to the version\n\ on the server. Other files have names corresponding to their IMAP UID.\n"; static const char README2[] = "\n\ The first 128 bytes of these files are used to store message attributes; the\n\ following data is equivalent to compress(1) output. So if you have to save a\n\ message by hand because of an emergency, throw away the first 128 bytes and\n\ decompress the rest, as e.g. 'dd if=MESSAGEFILE skip=1 bs=128 | zcat' does.\n"; static const char README3[] = "\n\ Files named QUEUE contain data that will be sent do the IMAP server next\n\ time a connection is made in online mode.\n"; static const char README4[] = "\n\ You can safely delete any file or directory here, unless it contains a QUEUE\n\ file that is not empty; mailx(1) will download the data again and will also\n\ write new cache entries if configured in this way. If you do not wish to use\n\ the cache anymore, delete the entire directory and unset the 'imap-cache'\n\ variable in mailx(1).\n"; static const char README5[] = "\n\ For more information about mailx(1), visit\n\ .\n"; static char * encname(struct mailbox *mp, const char *name, int same, const char *box) { char *cachedir, *eaccount, *emailbox, *ename, *res; int resz; ename = strenc(name); if (mp->mb_cache_directory && same && box == NULL) { res = salloc(resz = strlen(mp->mb_cache_directory) + strlen(ename) + 2); snprintf(res, resz, "%s%s%s", mp->mb_cache_directory, *ename ? "/" : "", ename); } else { if ((cachedir = value("imap-cache")) == NULL) return NULL; cachedir = expand(cachedir); eaccount = strenc(mp->mb_imap_account); if (box) emailbox = strenc(box); else if (asccasecmp(mp->mb_imap_mailbox, "INBOX")) emailbox = strenc(mp->mb_imap_mailbox); else emailbox = "INBOX"; res = salloc(resz = strlen(cachedir) + strlen(eaccount) + strlen(emailbox) + strlen(ename) + 4); snprintf(res, resz, "%s/%s/%s%s%s", cachedir, eaccount, emailbox, *ename ? "/" : "", ename); } return res; } static char * encuid(struct mailbox *mp, unsigned long uid) { char buf[30]; snprintf(buf, sizeof buf, "%lu", uid); return encname(mp, buf, 1, NULL); } enum okay getcache1(struct mailbox *mp, struct message *m, enum needspec need, int setflags) { FILE *fp; long n = 0, size = 0, xsize, xtime, xlines = -1, lines = 0; int lastc = EOF, i, xflag, inheader = 1; char b, iob[32768]; off_t offset; void *zp; if (setflags == 0 && ((mp->mb_type != MB_IMAP && mp->mb_type != MB_CACHE) || m->m_uid == 0)) return STOP; if ((fp = Fopen(encuid(mp, m->m_uid), "r")) == NULL) return STOP; fcntl_lock(fileno(fp), F_RDLCK); if (fscanf(fp, infofmt, &b, &xsize, &xflag, &xtime, &xlines) < 4) goto fail; if (need != NEED_UNSPEC) { switch (b) { case 'H': if (need == NEED_HEADER) goto success; goto fail; case 'B': if (need == NEED_HEADER || need == NEED_BODY) goto success; goto fail; default: goto fail; } } success: if (b == 'N') goto flags; fseek(fp, INITSKIP, SEEK_SET); zp = zalloc(fp); fseek(mp->mb_otf, 0L, SEEK_END); offset = ftell(mp->mb_otf); while (inheader && (n = zread(zp, iob, sizeof iob)) > 0) { size += n; for (i = 0; i < n; i++) { if (iob[i] == '\n') { lines++; if (lastc == '\n') inheader = 0; } lastc = iob[i]&0377; } fwrite(iob, 1, n, mp->mb_otf); } if (n > 0 && need == NEED_BODY) { while ((n = zread(zp, iob, sizeof iob)) > 0) { size += n; for (i = 0; i < n; i++) if (iob[i] == '\n') lines++; fwrite(iob, 1, n, mp->mb_otf); } } fflush(mp->mb_otf); if (zfree(zp) < 0 || n < 0 || ferror(fp) || ferror(mp->mb_otf)) goto fail; m->m_size = size; m->m_lines = lines; m->m_block = mailx_blockof(offset); m->m_offset = mailx_offsetof(offset); flags: if (setflags) { m->m_xsize = xsize; m->m_time = xtime; if (setflags & 2) { m->m_flag = xflag | MNOFROM; if (b != 'B') m->m_flag |= MHIDDEN; } } if (xlines > 0 && m->m_xlines <= 0) m->m_xlines = xlines; switch (b) { case 'B': m->m_xsize = xsize; if (xflag == MREAD && xlines > 0) m->m_flag |= MFULLYCACHED; if (need == NEED_BODY) { m->m_have |= HAVE_HEADER|HAVE_BODY; if (m->m_lines > 0) m->m_xlines = m->m_lines; break; } /*FALLTHRU*/ case 'H': m->m_have |= HAVE_HEADER; break; case 'N': break; } Fclose(fp); return OKAY; fail: Fclose(fp); return STOP; } enum okay getcache(struct mailbox *mp, struct message *m, enum needspec need) { return getcache1(mp, m, need, 0); } void putcache(struct mailbox *mp, struct message *m) { FILE *ibuf, *obuf; char *name, ob; int c, oflag; long n, count, oldoffset, osize, otime, olines = -1; char iob[32768]; void *zp; if ((mp->mb_type != MB_IMAP && mp->mb_type != MB_CACHE) || m->m_uid == 0 || m->m_time == 0 || (m->m_flag & (MTOUCH|MFULLYCACHED)) == MFULLYCACHED) return; if (m->m_have & HAVE_BODY) c = 'B'; else if (m->m_have & HAVE_HEADER) c = 'H'; else if (m->m_have == HAVE_NOTHING) c = 'N'; else return; oldoffset = ftell(mp->mb_itf); if ((obuf = Fopen(name = encuid(mp, m->m_uid), "r+")) == NULL) { if ((obuf = Fopen(name, "w")) == NULL) return; fcntl_lock(fileno(obuf), F_WRLCK); } else { fcntl_lock(fileno(obuf), F_WRLCK); if (fscanf(obuf, infofmt, &ob, &osize, &oflag, &otime, &olines) >= 4 && ob != '\0' && (ob == 'B' || (ob == 'H' && c != 'B'))) { if (m->m_xlines <= 0 && olines > 0) m->m_xlines = olines; if ((c != 'N' && osize != m->m_xsize) || oflag != USEBITS(m->m_flag) || otime != m->m_time || (m->m_xlines > 0 && olines != m->m_xlines)) { fflush(obuf); rewind(obuf); fprintf(obuf, infofmt, ob, (long)m->m_xsize, USEBITS(m->m_flag), (long)m->m_time, m->m_xlines); putc('\n', obuf); } Fclose(obuf); return; } fflush(obuf); rewind(obuf); ftruncate(fileno(obuf), 0); } if ((ibuf = setinput(mp, m, NEED_UNSPEC)) == NULL) { Fclose(obuf); return; } if (c == 'N') goto done; fseek(obuf, INITSKIP, SEEK_SET); zp = zalloc(obuf); count = m->m_size; while (count > 0) { n = count > sizeof iob ? sizeof iob : count; count -= n; if (fread(iob, 1, n, ibuf) != n || zwrite(zp, iob, n) != n) { unlink(name); zfree(zp); goto out; } } if (zfree(zp) < 0) { unlink(name); goto out; } done: rewind(obuf); fprintf(obuf, infofmt, c, (long)m->m_xsize, USEBITS(m->m_flag), (long)m->m_time, m->m_xlines); putc('\n', obuf); if (ferror(obuf)) { unlink(name); goto out; } if (c == 'B' && USEBITS(m->m_flag) == MREAD) m->m_flag |= MFULLYCACHED; out: if (Fclose(obuf) != 0) { m->m_flag &= ~MFULLYCACHED; unlink(name); } fseek(mp->mb_itf, oldoffset, SEEK_SET); } void initcache(struct mailbox *mp) { char *name, *uvname; FILE *uvfp; unsigned long uv; struct cw cw; free(mp->mb_cache_directory); mp->mb_cache_directory = NULL; if ((name = encname(mp, "", 1, NULL)) == NULL) return; mp->mb_cache_directory = sstrdup(name); if ((uvname = encname(mp, "UIDVALIDITY", 1, NULL)) == NULL) return; if (cwget(&cw) == STOP) return; if ((uvfp = Fopen(uvname, "r+")) == NULL || (fcntl_lock(fileno(uvfp), F_RDLCK), 0) || fscanf(uvfp, "%lu", &uv) != 1 || uv != mp->mb_uidvalidity) { if ((uvfp = clean(mp, &cw)) == NULL) goto out; } else { fflush(uvfp); rewind(uvfp); } fcntl_lock(fileno(uvfp), F_WRLCK); fprintf(uvfp, "%lu\n", mp->mb_uidvalidity); if (ferror(uvfp) || Fclose(uvfp) != 0) { unlink(uvname); mp->mb_uidvalidity = 0; } out: cwrelse(&cw); } void purgecache(struct mailbox *mp, struct message *m, long mc) { char *name; struct cw cw; if ((name = encname(mp, "", 1, NULL)) == NULL) return; if (cwget(&cw) == STOP) return; purge(mp, m, mc, &cw, name); cwrelse(&cw); } static FILE * clean(struct mailbox *mp, struct cw *cw) { char *cachedir, *eaccount, *emailbox, *buf; int bufsz; DIR *dirfd; struct dirent *dp; FILE *fp = NULL; if ((cachedir = value("imap-cache")) == NULL) return NULL; cachedir = expand(cachedir); eaccount = strenc(mp->mb_imap_account); if (asccasecmp(mp->mb_imap_mailbox, "INBOX")) emailbox = strenc(mp->mb_imap_mailbox); else emailbox = "INBOX"; buf = salloc(bufsz = strlen(cachedir) + strlen(eaccount) + strlen(emailbox) + 40); if (makedir(cachedir) != OKAY) return NULL; snprintf(buf, bufsz, "%s/README", cachedir); if ((fp = Fopen(buf, "wx")) != NULL) { fputs(README1, fp); fputs(README2, fp); fputs(README3, fp); fputs(README4, fp); fputs(README5, fp); Fclose(fp); } snprintf(buf, bufsz, "%s/%s", cachedir, eaccount); if (makedir(buf) != OKAY) return NULL; snprintf(buf, bufsz, "%s/%s/%s", cachedir, eaccount, emailbox); if (makedir(buf) != OKAY) return NULL; if (chdir(buf) < 0) return NULL; if ((dirfd = opendir(".")) == NULL) goto out; while ((dp = readdir(dirfd)) != NULL) { if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) continue; unlink(dp->d_name); } closedir(dirfd); fp = Fopen("UIDVALIDITY", "w"); out: if (cwret(cw) == STOP) { fputs("Fatal: Cannot change back to current directory.\n", stderr); abort(); } return fp; } static unsigned long * builds(long *contentelem) { unsigned long n, *contents = NULL; long contentalloc = 0; char *x; DIR *dirfd; struct dirent *dp; *contentelem = 0; if ((dirfd = opendir(".")) == NULL) return NULL; while ((dp = readdir(dirfd)) != NULL) { if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) continue; n = strtoul(dp->d_name, &x, 10); if (*x != '\0') continue; if (*contentelem >= contentalloc - 1) contents = srealloc(contents, (contentalloc += 200) * sizeof *contents); contents[(*contentelem)++] = n; } closedir(dirfd); if (*contentelem > 0) { contents[*contentelem] = 0; qsort(contents, *contentelem, sizeof *contents, longlt); } return contents; } static void purge(struct mailbox *mp, struct message *m, long mc, struct cw *cw, const char *name) { unsigned long *contents; long i, j, contentelem; if (chdir(name) < 0) return; contents = builds(&contentelem); if (contents) { i = j = 0; while (j < contentelem) { if (i < mc && m[i].m_uid == contents[j]) { i++; j++; } else if (i < mc && m[i].m_uid < contents[j]) i++; else remve(contents[j++]); } } if (cwret(cw) == STOP) { fputs("Fatal: Cannot change back to current directory.\n", stderr); abort(); } free(contents); } static int longlt(const void *a, const void *b) { return *(long *)a - *(long *)b; } static void remve(unsigned long n) { char buf[30]; snprintf(buf, sizeof buf, "%lu", n); unlink(buf); } void delcache(struct mailbox *mp, struct message *m) { char *fn; fn = encuid(mp, m->m_uid); if (fn && unlink(fn) == 0) m->m_flag |= MUNLINKED; } enum okay cache_setptr(int transparent) { int i; struct cw cw; char *name; unsigned long *contents; long contentelem; enum okay ok = STOP; struct message *omessage = NULL; int omsgCount = 0; if (transparent) { omessage = message; omsgCount = msgCount; } free(mb.mb_cache_directory); mb.mb_cache_directory = NULL; if ((name = encname(&mb, "", 1, NULL)) == NULL) return STOP; mb.mb_cache_directory = sstrdup(name); if (cwget(&cw) == STOP) return STOP; if (chdir(name) < 0) return STOP; contents = builds(&contentelem); msgCount = contentelem; message = scalloc(msgCount + 1, sizeof *message); if (cwret(&cw) == STOP) { fputs("Fatal: Cannot change back to current directory.\n", stderr); abort(); } cwrelse(&cw); for (i = 0; i < msgCount; i++) { message[i].m_uid = contents[i]; getcache1(&mb, &message[i], NEED_UNSPEC, 3); } ok = OKAY; if (ok == OKAY) { mb.mb_type = MB_CACHE; mb.mb_perm = Rflag ? 0 : MB_DELE; if (transparent) transflags(omessage, omsgCount, 1); else setdot(message); } return ok; } enum okay cache_list(struct mailbox *mp, const char *base, int strip, FILE *fp) { char *name, *cachedir, *eaccount; DIR *dirfd; struct dirent *dp; const char *cp, *bp, *sp; int namesz; if ((cachedir = value("imap-cache")) == NULL) return STOP; cachedir = expand(cachedir); eaccount = strenc(mp->mb_imap_account); name = salloc(namesz = strlen(cachedir) + strlen(eaccount) + 2); snprintf(name, namesz, "%s/%s", cachedir, eaccount); if ((dirfd = opendir(name)) == NULL) return STOP; while ((dp = readdir(dirfd)) != NULL) { if (dp->d_name[0] == '.') continue; cp = sp = strdec(dp->d_name); for (bp = base; *bp && *bp == *sp; bp++) sp++; if (*bp) continue; cp = strip ? sp : cp; fprintf(fp, "%s\n", *cp ? cp : "INBOX"); } closedir(dirfd); return OKAY; } enum okay cache_remove(const char *name) { struct stat st; DIR *dirfd; struct dirent *dp; char *path; int pathsize, pathend, n; char *dir; if ((dir = encname(&mb, "", 0, protfile(name))) == NULL) return OKAY; pathend = strlen(dir); path = smalloc(pathsize = pathend + 30); strcpy(path, dir); path[pathend++] = '/'; path[pathend] = '\0'; if ((dirfd = opendir(path)) == NULL) { free(path); return OKAY; } while ((dp = readdir(dirfd)) != NULL) { if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) continue; n = strlen(dp->d_name); if (pathend + n + 1 > pathsize) path = srealloc(path, pathsize = pathend + n + 30); strcpy(&path[pathend], dp->d_name); if (stat(path, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG) continue; if (unlink(path) < 0) { perror(path); closedir(dirfd); free(path); return STOP; } } closedir(dirfd); path[pathend] = '\0'; rmdir(path); /* no error on failure, might contain submailboxes */ free(path); return OKAY; } enum okay cache_rename(const char *old, const char *new) { char *olddir, *newdir; if ((olddir = encname(&mb, "", 0, protfile(old))) == NULL || (newdir = encname(&mb, "", 0, protfile(new))) == NULL) return OKAY; if (rename(olddir, newdir) < 0) { perror(olddir); return STOP; } return OKAY; } unsigned long cached_uidvalidity(struct mailbox *mp) { FILE *uvfp; char *uvname; unsigned long uv; if ((uvname = encname(mp, "UIDVALIDITY", 1, NULL)) == NULL) return 0; if ((uvfp = Fopen(uvname, "r")) == NULL || (fcntl_lock(fileno(uvfp), F_RDLCK), 0) || fscanf(uvfp, "%lu", &uv) != 1) uv = 0; Fclose(uvfp); return uv; } static FILE * cache_queue1(struct mailbox *mp, char *mode, char **xname) { char *name; FILE *fp; if ((name = encname(mp, "QUEUE", 0, NULL)) == NULL) return NULL; if ((fp = Fopen(name, mode)) != NULL) fcntl_lock(fileno(fp), F_WRLCK); if (xname) *xname = name; return fp; } FILE * cache_queue(struct mailbox *mp) { FILE *fp; fp = cache_queue1(mp, "a", NULL); if (fp == NULL) fputs("Cannot queue IMAP command. Retry when online.\n", stderr); return fp; } enum okay cache_dequeue(struct mailbox *mp) { int bufsz; char *cachedir, *eaccount, *buf, *oldbox; DIR *dirfd; struct dirent *dp; if ((cachedir = value("imap-cache")) == NULL) return OKAY; cachedir = expand(cachedir); eaccount = strenc(mp->mb_imap_account); buf = salloc(bufsz = strlen(cachedir) + strlen(eaccount) + 2); snprintf(buf, bufsz, "%s/%s", cachedir, eaccount); if ((dirfd = opendir(buf)) == NULL) return OKAY; oldbox = mp->mb_imap_mailbox; while ((dp = readdir(dirfd)) != NULL) { if (dp->d_name[0] == '.') continue; mp->mb_imap_mailbox = strdec(dp->d_name); dequeue1(mp); } closedir(dirfd); mp->mb_imap_mailbox = oldbox; return OKAY; } static enum okay dequeue1(struct mailbox *mp) { FILE *fp = NULL, *uvfp = NULL; char *qname, *uvname; unsigned long uv; off_t is_size; int is_count; fp = cache_queue1(mp, "r+", &qname); if (fp != NULL && fsize(fp) > 0) { if (imap_select(mp, &is_size, &is_count, mp->mb_imap_mailbox) != OKAY) { fprintf(stderr, "Cannot select \"%s\" for dequeuing.\n", mp->mb_imap_mailbox); goto save; } if ((uvname = encname(mp, "UIDVALIDITY", 0, NULL)) == NULL || (uvfp = Fopen(uvname, "r")) == NULL || (fcntl_lock(fileno(uvfp), F_RDLCK), 0) || fscanf(uvfp, "%lu", &uv) != 1 || uv != mp->mb_uidvalidity) { fprintf(stderr, "Unique identifiers for \"%s\" are out of date. " "Cannot commit IMAP commands.\n", mp->mb_imap_mailbox); save: fputs("Saving IMAP commands to dead.letter\n", stderr); savedeadletter(fp); ftruncate(fileno(fp), 0); Fclose(fp); if (uvfp) Fclose(uvfp); return STOP; } Fclose(uvfp); printf("Committing IMAP commands for \"%s\"\n", mp->mb_imap_mailbox); imap_dequeue(mp, fp); } if (fp) { Fclose(fp); unlink(qname); } return OKAY; } #endif /* HAVE_SOCKETS */ heirloom-mailx-12.5/catd/000077500000000000000000000000001155563371200153125ustar00rootroot00000000000000heirloom-mailx-12.5/catd/en_US000066400000000000000000000230651155563371200162540ustar00rootroot00000000000000$ Sccsid @(#)en_US 1.55 (gritter) 7/10/04 $quote $ $ Messages for nail, a mail user agent $set 1 1 $panic: $ 2 \n 3 Too much "sourcing" going on.\n 4 "Source" stack over-pop.\n 5 Unmatched "if"\n 6 No more mail.\n 7 On last screenful of messages\n 8 On first screenful of messages\n 9 Unrecognized scrolling command "%s"\n 10 --- DELETED --- 11 $%c%c%3d %s%-*.*s %16.16s %s\n$ 12 $%c%c%3d %s%-*.*s %16.16s %s %.*s\n$ 13 %d\n 14 Commands are:\n 15 $%s, $ 16 variable cmd not set\n 17 Message %2d:\n 18 No messages to pipe. 19 Message %2d:\n 20 No value set for "folder"\n 21 No messages applicable\n 22 At EOF\n 23 No messages to %s.\n 24 Cannot determine message sender to %s.\n 25 [Appended] 26 [New file] 27 binary 28 No file specified.\n 29 At EOF\n 30 No more messages\n 31 Okie dokie 32 $ -- Core dumped.\n$ 33 $ -- Can't dump core.\n$ 34 No fields currently being %s.\n 35 --- DELETED --- 36 $ Nail Commands\n\ t type messages\n\ n goto and type next message\n\ e edit messages\n\ f give head lines of messages\n\ d delete messages\n\ s file append messages to file\n\ u undelete messages\n\ R reply to message senders\n\ r reply to message senders and all recipients\n\ pre make messages go back to /var/mail\n\ m mail to specific users\n\ q quit, saving unresolved messages in mbox\n\ x quit, do not remove system mailbox\n\ h print out active message headers\n\ ! shell escape\n\ cd [directory] chdir to directory or home if none given\n\ \n\ A consists of integers, ranges of same, or user names separated\n\ by spaces. If omitted, Nail uses the last message typed.\n\ \n\ A consists of user names or aliases separated by spaces.\n\ Aliases are defined in .mailrc in your home directory.\n$ 37 Sorry, can't reply to multiple messages at once\n 38 --- DELETED --- 39 Cannot "preserve" in edit mode\n 40 %d: %d/%u\n 41 Non-null variable name required\n 42 Illegal nested "if"\n 43 Unrecognized if-keyword: "%s"\n 44 "Else" without matching "if"\n 45 Mail's idea of conditions is screwed up\n 46 "Endif" without matching "if"\n 47 No recipient specified. 48 No applicable messages. 49 $-----------------------------------------------------------\n\ The following ~ escapes are defined:\n\ ~~ Quote a single tilde\n\ ~@ [file ...] Edit attachment list\n\ ~b users Add users to "blind" cc list\n\ ~c users Add users to cc list\n\ ~d Read in dead.letter\n\ ~e Edit the message buffer\n\ ~f messages Read in messages\n\ ~F messages Same as ~f, but keep all header lines\n\ ~h Prompt for to list, subject and cc list\n\ ~r file Read a file into the message buffer\n\ ~p Print the message buffer\n\ ~m messages Read in messages, right shifted by a tab\n\ ~M messages Same as ~m, but keep all header lines\n\ ~s subject Set subject\n\ ~t users Add users to to list\n\ ~v Invoke display editor on message\n\ ~w file Write message onto file.\n\ ~? Print this message\n\ ~!command Invoke the shell\n\ ~|command Pipe the message through the command\n\ -----------------------------------------------------------\n$ 50 $#%u\tfilename: $ 51 temporary mail file 52 $ wrote:\n\n$ 53 \n(Interrupt -- one more to kill letter)\n 54 (continue)\n 55 Use "." to terminate letter\n 56 Unknown tilde escape.\n 57 Interpolate what file?\n 58 %s: Directory\n 59 $"%s" $ 60 %d/%d\n 61 Write what file!?\n 62 -------\nMessage contains:\n 63 Attachments: 64 File exists\n 65 %d/%ld\n 66 temporary mail edit file 67 No bytes from "%s" !?\n 68 No appropriate messages\n 69 Interpolating: 70 temporary mail file 71 %s seems a stale lock? Need to be removed by hand?\n 72 $Edit message %d [ynq]? $ 73 temporary mail edit file 74 cannot create tempfile\n 75 --- DELETED --- 76 --- DELETED --- 77 temporary file seek 78 --- DELETED --- 79 --- DELETED --- 80 No previous file\n 81 "%s": Expansion failed.\n 82 "%s": No match.\n 83 "%s": Expansion buffer overflow.\n 84 "%s": Ambiguous.\n 85 --- DELETED --- 86 --- DELETED --- 87 temporary mail message file 88 No mail for %s\n 89 Use "quit" to quit.\n 90 Can't "!" while sourcing\n 91 Unknown command: "%s"\n 92 May not execute "%s" while sending\n 93 May not execute "%s" while sourcing\n 94 May not execute "%s" -- message file is read only\n 95 Cannot recursively invoke "%s"\n 96 Illegal use of "message list"\n 97 No applicable messages\n 98 Illegal use of "message list"\n 99 %s requires at least %d arg(s)\n 100 %s takes no more than %d arg(s)\n 101 Unknown argtype 102 Interrupt\n 103 $"%s": $ 104 1 message 105 %d messages 106 $ %d new$ 107 $ %d unread$ 108 $ %d deleted$ 109 $ %d saved$ 110 $ [Read only]$ 111 Version %s\n 112 No numbers mixed with *\n 113 Non-numeric second argument\n 114 Referencing beyond EOF\n 115 Referencing before 1\n 116 Non-numeric second argument\n 117 Unknown colon modifier "%s"\n 118 Can't mix "*" with anything\n 119 No applicable messages.\n 120 No applicable messages from {%s 121 , %s 122 }\n 123 No messages satisfy 124 %d: Invalid message number\n 125 %d: Inappropriate message\n 126 Too many elements in the list; excess discarded.\n 127 Missing %c\n 128 Too many regrets 129 Bad message number to mark 130 Bad message number to unmark 131 No applicable messages\n 132 No applicable messages\n 133 %d: Inappropriate message\n 134 Unknown metachar (%c)\n 135 Usage: %s -eiIUdFntBNHV~ -T FILE -u USER -h hops -r address -s SUBJECT -a FILE -q FILE -f FILE -b USERS -c USERS users\n 136 --- DELETED --- 137 Cannot give -f and people to send to.\n 138 Send options without primary recipient specified.\n 139 --- DELETED --- 140 $%s version %s. Type ? for help.\n$ 141 \nInterrupt\n 142 %s contains invalid @@ sequence\n 143 %s contains invalid character ' 144 '\n 145 detract asked to insert commas\n 146 temporary edit file 147 temporary edit file 148 temporary edit file 149 Can't reopen image\n 150 Expanding alias to depth larger than %d\n 151 No names to unpack 152 Internal error: bad stdio open mode %s\n 153 Invalid file pointer 154 Fatal error in process.\n 155 Held 1 message in %s\n 156 Held %d messages in %s\n 157 Unable to lock mailbox 158 New mail has arrived.\n 159 --- DELETED --- 160 --- DELETED --- 161 --- DELETED --- 162 temporary mail quit file 163 temporary mail quit file 164 Saved 1 message in mbox\n 165 Saved %d messages in mbox\n 166 Thou hast new mail.\n 167 tmpfile 168 $"%s" $ 169 removed\n 170 complete\n 171 $Enter filename for part $ 172 $: $ 173 tmpfile 174 $\nPart $ 175 :\n 176 Cannot open %s\n 177 Warning: invalid encoding %s, using 8bit\n 178 temporary mail file 179 Cannot convert from %s to %s\n 180 temporary mail file 181 Sendmail arguments: 182 . . . message not sent.\n 183 EOT\n 184 No message, no subject; hope that's ok\n 185 Null message body; hope that's ok\n 186 No recipients specified\n 187 . . . message not sent.\n 188 temporary mail file 189 temporary mail file 190 . . . message not sent.\n 191 smtp-server: %s 192 Connecting to %s . . . 193 $ connected.\n$ 194 No SMTP support compiled in.\n 195 String too large 196 --- DELETED --- 197 --- DELETED --- 198 "%s" is not a user of this system\n 199 user = %s, homedir = %s\n 200 too long to edit\n 201 Cannot associate a name with uid %d\n 202 "%s": not a group\n 203 "%s": undefined variable\n 204 Need -f with -I.\n 205 More than one file given with -f\n 206 $%c%c%3d %s%-*.*s %16.16s %s\n$ 207 $%c%c%3d %s%-*.*s %16.16s %s "%.*s"\n$ 208 warning: incomplete line - newline appended\n 209 Must specify alias or group to remove\n 210 [Binary content]\n\n 211 removed.\n 212 updated.\n 213 214 Loaded 1 new message\n 215 Loaded %d new messages\n 216 No POP3 support compiled in.\n 217 Cannot handle protocol: %s\n 218 POP3 error: %s 219 POP3 connection already closed.\n 220 expasion name for shortcut missing\n 221 too many arguments\n 222 need shortcut names to remove\n 223 %s: no such shortcut\n 224 --- DELETED --- 225 No SSL support compiled in.\n 226 Cannot determine parent Message-ID of the current message\n 227 Parent message not found\n 228 No previously current message\n 229 Error with certificate at depth: %i\n 230 $ issuer = %s\n$ 231 $ subject = %s\n$ 232 $ err %i: %s\n$ 233 Error loading 234 $ %s$ 235 $ or$ 236 $ %s$ 237 \n 238 cannot load private key from file %s\n 239 cannot load certificate from file %s\n 240 invalid ciphers: %s\n 241 Unexpected EOF on SMTP connection\n 242 Syntax error in "%s"\n 243 Error loading default CA locations\n 244 Invalid SSL method "%s"\n 245 entropy daemon at "%s" not available\n 246 entropy file at "%s" not available\n 247 writing entropy data to "%s" failed\n 248 no certificate from "%s"\n 249 host certificate does not match "%s"\n 250 POP3 read failed 251 unknown service: %s\n 252 could not resolve host: %s\n 253 could not create socket 254 could not connect 255 SMTP read failed 256 SMTP write error 257 Cannot execute "%s" without active mailbox\n 258 No mail at %s\n 259 POP3 write error 260 invalid POP3 STAT response: %s\n 261 SSL_CTX_new() failed 262 SSL_new() failed 263 could not initiate SSL/TLS connection 264 $Continue (y/n)? $ 265 invalid value of ssl-verify: %s\n 266 Ignoring header field "%s"\n 267 Restoring deleted header lines\n 268 Pipe to: "%s"\n heirloom-mailx-12.5/cmd1.c000066400000000000000000000560461155563371200154020ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /*- * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)cmd1.c 2.97 (gritter) 6/16/07"; #endif #endif /* not lint */ #include "rcv.h" #include "extern.h" #ifdef HAVE_WCWIDTH #include #endif /* * Mail -- a mail program * * User commands. */ /* * Print the current active headings. * Don't change dot if invoker didn't give an argument. */ static int screen; static void onpipe(int signo); static int dispc(struct message *mp, const char *a); static int scroll1(char *arg, int onlynew); static void hprf(const char *fmt, int mesg, FILE *f, int threaded, const char *attrlist); static int putindent(FILE *fp, struct message *mp, int maxwidth); static int type1(int *msgvec, int doign, int page, int pipe, int decode, char *cmd, off_t *tstats); static int pipe1(char *str, int doign); void brokpipe(int signo); char * get_pager(void) { char *cp; cp = value("PAGER"); if (cp == NULL || *cp == '\0') cp = value("bsdcompat") ? "more" : "pg"; return cp; } int headers(void *v) { int *msgvec = v; int g, k, n, mesg, flag = 0, lastg = 1; struct message *mp, *mq, *lastmq = NULL; int size; enum mflag fl = MNEW|MFLAGGED; size = screensize(); n = msgvec[0]; /* n == {-2, -1, 0}: called from scroll() */ if (screen < 0) screen = 0; k = screen * size; if (k >= msgCount) k = msgCount - size; if (k < 0) k = 0; if (mb.mb_threaded == 0) { g = 0; mq = &message[0]; for (mp = &message[0]; mp < &message[msgCount]; mp++) if (visible(mp)) { if (g % size == 0) mq = mp; if (mp->m_flag&fl) { lastg = g; lastmq = mq; } if (n>0 && mp==&message[n-1] || n==0 && g==k || n==-2 && g==k+size && lastmq || n<0 && g>=k && mp->m_flag&fl) break; g++; } if (lastmq && (n==-2 || n==-1 && mp==&message[msgCount])) { g = lastg; mq = lastmq; } screen = g / size; mp = mq; mesg = mp - &message[0]; if (dot != &message[n-1]) { for (mq = mp; mq < &message[msgCount]; mq++) if (visible(mq)) { setdot(mq); break; } } if (mb.mb_type == MB_IMAP) imap_getheaders(mesg+1, mesg + size); for (; mp < &message[msgCount]; mp++) { mesg++; if (!visible(mp)) continue; if (flag++ >= size) break; printhead(mesg, stdout, 0); } } else { /* threaded */ g = 0; mq = threadroot; for (mp = threadroot; mp; mp = next_in_thread(mp)) if (visible(mp) && (mp->m_collapsed <= 0 || mp == &message[n-1])) { if (g % size == 0) mq = mp; if (mp->m_flag&fl) { lastg = g; lastmq = mq; } if (n>0 && mp==&message[n-1] || n==0 && g==k || n==-2 && g==k+size && lastmq || n<0 && g>=k && mp->m_flag&fl) break; g++; } if (lastmq && (n==-2 || n==-1 && mp==&message[msgCount])) { g = lastg; mq = lastmq; } screen = g / size; mp = mq; if (dot != &message[n-1]) { for (mq = mp; mq; mq = next_in_thread(mq)) if (visible(mq) && mq->m_collapsed <= 0) { setdot(mq); break; } } while (mp) { if (visible(mp) && (mp->m_collapsed <= 0 || mp == &message[n-1])) { if (flag++ >= size) break; printhead(mp - &message[0] + 1, stdout, mb.mb_threaded); } mp = next_in_thread(mp); } } if (flag == 0) { printf(catgets(catd, CATSET, 6, "No more mail.\n")); return(1); } return(0); } /* * Scroll to the next/previous screen */ int scroll(void *v) { return scroll1(v, 0); } int Scroll(void *v) { return scroll1(v, 1); } static int scroll1(char *arg, int onlynew) { int size; int cur[1]; cur[0] = onlynew ? -1 : 0; size = screensize(); switch (*arg) { case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '0': screen = atoi(arg); goto scroll_forward; case '\0': screen++; goto scroll_forward; case '$': screen = msgCount / size; goto scroll_forward; case '+': if (arg[1] == '\0') screen++; else screen += atoi(arg + 1); scroll_forward: if (screen * size > msgCount) { screen = msgCount / size; printf(catgets(catd, CATSET, 7, "On last screenful of messages\n")); } break; case '-': if (arg[1] == '\0') screen--; else screen -= atoi(arg + 1); if (screen < 0) { screen = 0; printf(catgets(catd, CATSET, 8, "On first screenful of messages\n")); } if (cur[0] == -1) cur[0] = -2; break; default: printf(catgets(catd, CATSET, 9, "Unrecognized scrolling command \"%s\"\n"), arg); return(1); } return(headers(cur)); } /* * Compute screen size. */ int screensize(void) { int s; char *cp; if ((cp = value("screen")) != NULL && (s = atoi(cp)) > 0) return s; return scrnheight - 4; } static sigjmp_buf pipejmp; /*ARGSUSED*/ static void onpipe(int signo) { siglongjmp(pipejmp, 1); } /* * Print out the headlines for each message * in the passed message list. */ int from(void *v) { int *msgvec = v; int *ip, n; FILE *obuf = stdout; char *cp; (void)&obuf; (void)&cp; if (is_a_tty[0] && is_a_tty[1] && (cp = value("crt")) != NULL) { for (n = 0, ip = msgvec; *ip; ip++) n++; if (n > (*cp == '\0' ? screensize() : atoi(cp)) + 3) { cp = get_pager(); if (sigsetjmp(pipejmp, 1)) goto endpipe; if ((obuf = Popen(cp, "w", NULL, 1)) == NULL) { perror(cp); obuf = stdout; } else safe_signal(SIGPIPE, onpipe); } } for (ip = msgvec; *ip != 0; ip++) printhead(*ip, obuf, mb.mb_threaded); if (--ip >= msgvec) setdot(&message[*ip - 1]); endpipe: if (obuf != stdout) { safe_signal(SIGPIPE, SIG_IGN); Pclose(obuf); safe_signal(SIGPIPE, dflpipe); } return(0); } static int dispc(struct message *mp, const char *a) { int dispc = ' '; /* * Bletch! */ if ((mp->m_flag & (MREAD|MNEW)) == MREAD) dispc = a[3]; if ((mp->m_flag & (MREAD|MNEW)) == (MREAD|MNEW)) dispc = a[2]; if (mp->m_flag & MANSWERED) dispc = a[8]; if (mp->m_flag & MDRAFTED) dispc = a[9]; if ((mp->m_flag & (MREAD|MNEW)) == MNEW) dispc = a[0]; if ((mp->m_flag & (MREAD|MNEW)) == 0) dispc = a[1]; if (mp->m_flag & MJUNK) dispc = a[13]; if (mp->m_flag & MSAVED) dispc = a[4]; if (mp->m_flag & MPRESERVE) dispc = a[5]; if (mp->m_flag & (MBOX|MBOXED)) dispc = a[6]; if (mp->m_flag & MFLAGGED) dispc = a[7]; if (mp->m_flag & MKILL) dispc = a[10]; if (mb.mb_threaded == 1 && mp->m_collapsed > 0) dispc = a[12]; if (mb.mb_threaded == 1 && mp->m_collapsed < 0) dispc = a[11]; return dispc; } static void hprf(const char *fmt, int mesg, FILE *f, int threaded, const char *attrlist) { struct message *mp = &message[mesg-1]; char *headline = NULL, *subjline, *name, *cp, *pbuf = NULL; struct headline hl; size_t headsize = 0; const char *fp; int B, c, i, n, s; int headlen = 0; struct str in, out; int subjlen = scrnwidth, fromlen, isto = 0, isaddr = 0; FILE *ibuf; if ((mp->m_flag & MNOFROM) == 0) { if ((ibuf = setinput(&mb, mp, NEED_HEADER)) == NULL) return; if ((headlen = readline(ibuf, &headline, &headsize)) < 0) return; } if ((subjline = hfield("subject", mp)) == NULL) subjline = hfield("subj", mp); if (subjline == NULL) { out.s = NULL; out.l = 0; } else { in.s = subjline; in.l = strlen(subjline); mime_fromhdr(&in, &out, TD_ICONV | TD_ISPR); subjline = out.s; } if ((mp->m_flag & MNOFROM) == 0) { pbuf = ac_alloc(headlen + 1); parse(headline, headlen, &hl, pbuf); } else { hl.l_from = /*fakefrom(mp);*/NULL; hl.l_tty = NULL; hl.l_date = fakedate(mp->m_time); } if (value("datefield") && (cp = hfield("date", mp)) != NULL) hl.l_date = fakedate(rfctime(cp)); if (Iflag) { if ((name = hfield("newsgroups", mp)) == NULL) if ((name = hfield("article-id", mp)) == NULL) name = "<>"; name = prstr(name); } else if (value("show-rcpt") == NULL) { name = name1(mp, 0); isaddr = 1; if (value("showto") && name && is_myname(skin(name))) { if ((cp = hfield("to", mp)) != NULL) { name = cp; isto = 1; } } } else { isaddr = 1; if ((name = hfield("to", mp)) != NULL) isto = 1; } if (name == NULL) { name = ""; isaddr = 0; } if (isaddr) { if (value("showname")) name = realname(name); else { name = prstr(skin(name)); } } for (fp = fmt; *fp; fp++) { if (*fp == '%') { if (*++fp == '-') { fp++; } else if (*fp == '+') fp++; while (digitchar(*fp&0377)) fp++; if (*fp == '\0') break; } else { #if defined (HAVE_MBTOWC) && defined (HAVE_WCWIDTH) if (mb_cur_max > 1) { wchar_t wc; if ((s = mbtowc(&wc, fp, mb_cur_max)) < 0) n = s = 1; else { if ((n = wcwidth(wc)) < 0) n = 1; } } else #endif /* HAVE_MBTOWC && HAVE_WCWIDTH */ { n = s = 1; } subjlen -= n; while (--s > 0) fp++; } } for (fp = fmt; *fp; fp++) { if (*fp == '%') { B = 0; n = 0; s = 1; if (*++fp == '-') { s = -1; fp++; } else if (*fp == '+') fp++; if (digitchar(*fp&0377)) { do n = 10*n + *fp - '0'; while (fp++, digitchar(*fp&0377)); } if (*fp == '\0') break; n *= s; switch (*fp) { case '%': putc('%', f); subjlen--; break; case '>': case '<': c = dot == mp ? *fp&0377 : ' '; putc(c, f); subjlen--; break; case 'a': c = dispc(mp, attrlist); putc(c, f); subjlen--; break; case 'm': if (n == 0) { n = 3; if (threaded) for (i=msgCount; i>999; i/=10) n++; } subjlen -= fprintf(f, "%*d", n, mesg); break; case 'f': if (n <= 0) n = 18; fromlen = n; if (isto) fromlen -= 3; fprintf(f, "%s%s", isto ? "To " : "", colalign(name, fromlen, 1)); subjlen -= n; break; case 'd': if (n <= 0) n = 16; subjlen -= fprintf(f, "%*.*s", n, n, hl.l_date); break; case 'l': if (n == 0) n = 4; if (mp->m_xlines) subjlen -= fprintf(f, "%*ld", n, mp->m_xlines); else { subjlen -= n; while (n--) putc(' ', f); } break; case 'o': if (n == 0) n = -5; subjlen -= fprintf(f, "%*lu", n, (long)mp->m_xsize); break; case 'i': if (threaded) subjlen -= putindent(f, mp, scrnwidth - 60); break; case 'S': B = 1; /*FALLTHRU*/ case 's': n = n>0 ? n : subjlen - 2; if (B) n -= 2; if (subjline != NULL && n >= 0) { /* pretty pathetic */ fprintf(f, B ? "\"%s\"" : "%s", colalign(subjline, n, 0)); } break; case 'U': if (n == 0) n = 9; subjlen -= fprintf(f, "%*lu", n, mp->m_uid); break; case 'e': if (n == 0) n = 2; subjlen -= fprintf(f, "%*u", n, threaded == 1 ? mp->m_level : 0); break; case 't': if (n == 0) { n = 3; if (threaded) for (i=msgCount; i>999; i/=10) n++; } fprintf(f, "%*ld", n, threaded ? mp->m_threadpos : mesg); subjlen -= n; break; case 'c': if (n == 0) n = 6; subjlen -= fprintf(f, "%*g", n, mp->m_score); break; } } else putc(*fp&0377, f); } putc('\n', f); if (out.s) free(out.s); if (headline) free(headline); if (pbuf) ac_free(pbuf); } /* * Print out the indenting in threaded display. */ static int putindent(FILE *fp, struct message *mp, int maxwidth) { struct message *mq; int indent, i; int *us; char *cs; int important = MNEW|MFLAGGED; if (mp->m_level == 0) return 0; cs = ac_alloc(mp->m_level); us = ac_alloc(mp->m_level * sizeof *us); i = mp->m_level - 1; if (mp->m_younger && mp->m_younger->m_level == i + 1) { if (mp->m_parent && mp->m_parent->m_flag & important) us[i] = mp->m_flag & important ? 0x2523 : 0x2520; else us[i] = mp->m_flag & important ? 0x251D : 0x251C; cs[i] = '+'; } else { if (mp->m_parent && mp->m_parent->m_flag & important) us[i] = mp->m_flag & important ? 0x2517 : 0x2516; else us[i] = mp->m_flag & important ? 0x2515 : 0x2514; cs[i] = '\\'; } mq = mp->m_parent; for (i = mp->m_level - 2; i >= 0; i--) { if (mq) { if (i > mq->m_level - 1) { us[i] = cs[i] = ' '; continue; } if (mq->m_younger) { if (mq->m_parent && mq->m_parent->m_flag&important) us[i] = 0x2503; else us[i] = 0x2502; cs[i] = '|'; } else us[i] = cs[i] = ' '; mq = mq->m_parent; } else us[i] = cs[i] = ' '; } for (indent = 0; indent < mp->m_level && indent < maxwidth; indent++) { if (indent < maxwidth - 1) putuc(us[indent], cs[indent] & 0377, fp); else putuc(0x21B8, '^', fp); } ac_free(us); ac_free(cs); return indent; } /* * Print out the header of a specific message. * This is a slight improvement to the standard one. */ void printhead(int mesg, FILE *f, int threaded) { int bsdflags, bsdheadline, sz; char *fmt, attrlist[30], *cp; bsdflags = value("bsdcompat") != NULL || value("bsdflags") != NULL || getenv("SYSV3") != NULL; strcpy(attrlist, bsdflags ? "NU *HMFATK+-J" : "NUROSPMFATK+-J"); if ((cp = value("attrlist")) != NULL) { sz = strlen(cp); if (sz > sizeof attrlist - 1) sz = sizeof attrlist - 1; memcpy(attrlist, cp, sz); } bsdheadline = value("bsdcompat") != NULL || value("bsdheadline") != NULL; if ((fmt = value("headline")) == NULL) fmt = bsdheadline ? "%>%a%m %20f %16d %3l/%-5o %i%S" : "%>%a%m %18f %16d %4l/%-5o %i%s"; hprf(fmt, mesg, f, threaded, attrlist); } /* * Print out the value of dot. */ /*ARGSUSED*/ int pdot(void *v) { printf(catgets(catd, CATSET, 13, "%d\n"), (int)(dot - &message[0] + 1)); return(0); } /* * Print out all the possible commands. */ /*ARGSUSED*/ int pcmdlist(void *v) { extern const struct cmd cmdtab[]; const struct cmd *cp; int cc; printf(catgets(catd, CATSET, 14, "Commands are:\n")); for (cc = 0, cp = cmdtab; cp->c_name != NULL; cp++) { cc += strlen(cp->c_name) + 2; if (cc > 72) { printf("\n"); cc = strlen(cp->c_name) + 2; } if ((cp+1)->c_name != NULL) printf(catgets(catd, CATSET, 15, "%s, "), cp->c_name); else printf("%s\n", cp->c_name); } return(0); } /* * Type out the messages requested. */ static sigjmp_buf pipestop; static int type1(int *msgvec, int doign, int page, int pipe, int decode, char *cmd, off_t *tstats) { int *ip; struct message *mp; char *cp; int nlines; off_t mstats[2]; /* * Must be static to become excluded from sigsetjmp(). */ static FILE *obuf; #ifdef __GNUC__ /* Avoid longjmp clobbering */ (void) &cp; (void) &cmd; (void) &obuf; #endif obuf = stdout; if (sigsetjmp(pipestop, 1)) goto close_pipe; if (pipe) { cp = value("SHELL"); if (cp == NULL) cp = SHELL; obuf = Popen(cmd, "w", cp, 1); if (obuf == NULL) { perror(cmd); obuf = stdout; } else { safe_signal(SIGPIPE, brokpipe); } } else if (value("interactive") != NULL && (page || (cp = value("crt")) != NULL)) { nlines = 0; if (!page) { for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { if ((message[*ip-1].m_have & HAVE_BODY) == 0) { if ((get_body(&message[*ip - 1])) != OKAY) return 1; } nlines += message[*ip - 1].m_lines; } } if (page || nlines > (*cp ? atoi(cp) : realscreenheight)) { cp = get_pager(); obuf = Popen(cp, "w", NULL, 1); if (obuf == NULL) { perror(cp); obuf = stdout; } else safe_signal(SIGPIPE, brokpipe); } } for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) { mp = &message[*ip - 1]; touch(mp); setdot(mp); uncollapse1(mp, 1); if (value("quiet") == NULL) fprintf(obuf, catgets(catd, CATSET, 17, "Message %2d:\n"), *ip); send(mp, obuf, doign ? ignore : 0, NULL, pipe && value("piperaw") ? SEND_MBOX : decode ? SEND_SHOW : doign ? SEND_TODISP : SEND_TODISP_ALL, mstats); if (pipe && value("page")) { putc('\f', obuf); } if (tstats) { tstats[0] += mstats[0]; tstats[1] += mstats[1]; } } close_pipe: if (obuf != stdout) { /* * Ignore SIGPIPE so it can't cause a duplicate close. */ safe_signal(SIGPIPE, SIG_IGN); Pclose(obuf); safe_signal(SIGPIPE, dflpipe); } return(0); } /* * Get the last, possibly quoted part of linebuf. */ char * laststring(char *linebuf, int *flag, int strip) { char *cp, *p; char quoted; *flag = 1; cp = strlen(linebuf) + linebuf - 1; /* * Strip away trailing blanks. */ while (cp > linebuf && whitechar(*cp & 0377)) cp--; *++cp = 0; if (cp == linebuf) { *flag = 0; return NULL; } /* * Now search for the beginning of the command name. */ quoted = *(cp - 1); if (quoted == '\'' || quoted == '\"') { cp--; if (strip) *cp = '\0'; cp--; while (cp > linebuf) { if (*cp != quoted) { cp--; } else if (*(cp - 1) != '\\') { break; } else { p = --cp; do { *p = *(p + 1); } while (*p++); cp--; } } if (cp == linebuf) *flag = 0; if (*cp == quoted) { if (strip) *cp++ = 0; } else *flag = 0; } else { while (cp > linebuf && !whitechar(*cp & 0377)) cp--; if (whitechar(*cp & 0377)) *cp++ = 0; else *flag = 0; } if (*cp == '\0') { return(NULL); } return(cp); } /* * Pipe the messages requested. */ static int pipe1(char *str, int doign) { char *cmd; int f, *msgvec, ret; off_t stats[2]; /*LINTED*/ msgvec = (int *)salloc((msgCount + 2) * sizeof *msgvec); if ((cmd = laststring(str, &f, 1)) == NULL) { cmd = value("cmd"); if (cmd == NULL || *cmd == '\0') { fputs(catgets(catd, CATSET, 16, "variable cmd not set\n"), stderr); return 1; } } if (!f) { *msgvec = first(0, MMNORM); if (*msgvec == 0) { if (inhook) return 0; puts(catgets(catd, CATSET, 18, "No messages to pipe.")); return 1; } msgvec[1] = 0; } else if (getmsglist(str, msgvec, 0) < 0) return 1; if (*msgvec == 0) { if (inhook) return 0; printf("No applicable messages.\n"); return 1; } printf(catgets(catd, CATSET, 268, "Pipe to: \"%s\"\n"), cmd); stats[0] = stats[1] = 0; if ((ret = type1(msgvec, doign, 0, 1, 0, cmd, stats)) == 0) { printf("\"%s\" ", cmd); if (stats[0] >= 0) printf("%lu", (long)stats[0]); else printf(catgets(catd, CATSET, 27, "binary")); printf("/%lu\n", (long)stats[1]); } return ret; } /* * Paginate messages, honor ignored fields. */ int more(void *v) { int *msgvec = v; return (type1(msgvec, 1, 1, 0, 0, NULL, NULL)); } /* * Paginate messages, even printing ignored fields. */ int More(void *v) { int *msgvec = v; return (type1(msgvec, 0, 1, 0, 0, NULL, NULL)); } /* * Type out messages, honor ignored fields. */ int type(void *v) { int *msgvec = v; return(type1(msgvec, 1, 0, 0, 0, NULL, NULL)); } /* * Type out messages, even printing ignored fields. */ int Type(void *v) { int *msgvec = v; return(type1(msgvec, 0, 0, 0, 0, NULL, NULL)); } /* * Show MIME-encoded message text, including all fields. */ int show(void *v) { int *msgvec = v; return(type1(msgvec, 0, 0, 0, 1, NULL, NULL)); } /* * Pipe messages, honor ignored fields. */ int pipecmd(void *v) { char *str = v; return(pipe1(str, 1)); } /* * Pipe messages, not respecting ignored fields. */ int Pipecmd(void *v) { char *str = v; return(pipe1(str, 0)); } /* * Respond to a broken pipe signal -- * probably caused by quitting more. */ /*ARGSUSED*/ void brokpipe(int signo) { siglongjmp(pipestop, 1); } /* * Print the top so many lines of each desired message. * The number of lines is taken from the variable "toplines" * and defaults to 5. */ int top(void *v) { int *msgvec = v; int *ip; struct message *mp; int c, topl, lines, lineb; char *valtop, *linebuf = NULL; size_t linesize; FILE *ibuf; topl = 5; valtop = value("toplines"); if (valtop != NULL) { topl = atoi(valtop); if (topl < 0 || topl > 10000) topl = 5; } lineb = 1; for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { mp = &message[*ip - 1]; touch(mp); setdot(mp); did_print_dot = 1; if (value("quiet") == NULL) printf(catgets(catd, CATSET, 19, "Message %2d:\n"), *ip); if (mp->m_flag & MNOFROM) printf("From %s %s\n", fakefrom(mp), fakedate(mp->m_time)); if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL) /* XXX could use TOP */ return 1; c = mp->m_lines; if (!lineb) printf("\n"); for (lines = 0; lines < c && lines <= topl; lines++) { if (readline(ibuf, &linebuf, &linesize) < 0) break; puts(linebuf); lineb = blankline(linebuf); } } if (linebuf) free(linebuf); return(0); } /* * Touch all the given messages so that they will * get mboxed. */ int stouch(void *v) { int *msgvec = v; int *ip; for (ip = msgvec; *ip != 0; ip++) { setdot(&message[*ip-1]); dot->m_flag |= MTOUCH; dot->m_flag &= ~MPRESERVE; /* * POSIX interpretation necessary. */ did_print_dot = 1; } return(0); } /* * Make sure all passed messages get mboxed. */ int mboxit(void *v) { int *msgvec = v; int *ip; for (ip = msgvec; *ip != 0; ip++) { setdot(&message[*ip-1]); dot->m_flag |= MTOUCH|MBOX; dot->m_flag &= ~MPRESERVE; /* * POSIX interpretation necessary. */ did_print_dot = 1; } return(0); } /* * List the folders the user currently has. */ int folders(void *v) { char **argv = v; char dirname[PATHSIZE]; char *cmd, *name; if (*argv) name = expand(*argv); else if (getfold(dirname, sizeof dirname) < 0) { printf(catgets(catd, CATSET, 20, "No value set for \"folder\"\n")); return 1; } else name = dirname; if (which_protocol(name) == PROTO_IMAP) imap_folders(name, *argv == NULL); else { if ((cmd = value("LISTER")) == NULL) cmd = "ls"; run_command(cmd, 0, -1, -1, name, NULL, NULL); } return 0; } heirloom-mailx-12.5/cmd2.c000066400000000000000000000412771155563371200154030ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)cmd2.c 2.47 (gritter) 5/9/10"; #endif #endif /* not lint */ #include "rcv.h" #include "extern.h" #include #include #include /* * Mail -- a mail program * * More user commands. */ static int save1(char *str, int mark, char *cmd, struct ignoretab *ignore, int convert, int sender_record, int domove); static char *snarf(char *linebuf, int *flag, int usembox); static int delm(int *msgvec); static int ignore1(char **list, struct ignoretab *tab, char *which); static int igshow(struct ignoretab *tab, char *which); static int igcomp(const void *l, const void *r); static void unignore_one(const char *name, struct ignoretab *tab); static int unignore1(char **list, struct ignoretab *tab, char *which); /* * If any arguments were given, go to the next applicable argument * following dot, otherwise, go to the next applicable message. * If given as first command with no arguments, print first message. */ int next(void *v) { int *msgvec = v; struct message *mp; int *ip, *ip2; int list[2], mdot; if (*msgvec != 0) { /* * If some messages were supplied, find the * first applicable one following dot using * wrap around. */ mdot = dot - &message[0] + 1; /* * Find the first message in the supplied * message list which follows dot. */ for (ip = msgvec; *ip != 0; ip++) { #ifdef _CRAY /* * Work around an optimizer bug in Cray Standard C Version 4.0.3 (057126). * Otherwise, SIGFPE is received when mb.mb_threaded != 0. */ #pragma _CRI suppress ip #endif /* _CRAY */ if (mb.mb_threaded ? message[*ip-1].m_threadpos > dot->m_threadpos : *ip > mdot) break; } if (*ip == 0) ip = msgvec; ip2 = ip; do { mp = &message[*ip2 - 1]; if ((mp->m_flag & (MDELETED|MHIDDEN)) == 0) { setdot(mp); goto hitit; } if (*ip2 != 0) ip2++; if (*ip2 == 0) ip2 = msgvec; } while (ip2 != ip); printf(catgets(catd, CATSET, 21, "No messages applicable\n")); return(1); } /* * If this is the first command, select message 1. * Note that this must exist for us to get here at all. */ if (!sawcom) { if (msgCount == 0) goto ateof; goto hitit; } /* * Just find the next good message after dot, no * wraparound. */ if (mb.mb_threaded == 0) { for (mp = dot + did_print_dot; mp < &message[msgCount]; mp++) if ((mp->m_flag & (MDELETED|MSAVED|MHIDDEN|MKILL)) == 0) break; } else { mp = dot; if (did_print_dot) mp = next_in_thread(mp); while (mp && mp->m_flag & (MDELETED|MSAVED|MHIDDEN|MKILL)) mp = next_in_thread(mp); } if (mp == NULL || mp >= &message[msgCount]) { ateof: printf(catgets(catd, CATSET, 22, "At EOF\n")); return(0); } setdot(mp); hitit: /* * Print dot. */ list[0] = dot - &message[0] + 1; list[1] = 0; return(type(list)); } /* * Save a message in a file. Mark the message as saved * so we can discard when the user quits. */ int save(void *v) { char *str = v; return save1(str, 1, "save", saveignore, SEND_MBOX, 0, 0); } int Save(void *v) { char *str = v; return save1(str, 1, "save", saveignore, SEND_MBOX, 1, 0); } /* * Copy a message to a file without affected its saved-ness */ int copycmd(void *v) { char *str = v; return save1(str, 0, "copy", saveignore, SEND_MBOX, 0, 0); } int Copycmd(void *v) { char *str = v; return save1(str, 0, "copy", saveignore, SEND_MBOX, 1, 0); } /* * Move a message to a file. */ int cmove(void *v) { char *str = v; return save1(str, 0, "move", saveignore, SEND_MBOX, 0, 1); } int cMove(void *v) { char *str = v; return save1(str, 0, "move", saveignore, SEND_MBOX, 1, 1); } /* * Decrypt and copy a message to a file. */ int cdecrypt(void *v) { char *str = v; return save1(str, 0, "decrypt", saveignore, SEND_DECRYPT, 0, 0); } int cDecrypt(void *v) { char *str = v; return save1(str, 0, "decrypt", saveignore, SEND_DECRYPT, 1, 0); } /* * Save/copy the indicated messages at the end of the passed file name. * If mark is true, mark the message "saved." */ static int save1(char *str, int mark, char *cmd, struct ignoretab *ignore, int convert, int sender_record, int domove) { struct stat st; int *ip; struct message *mp; char *file = NULL, *disp = ""; int f, *msgvec; FILE *obuf; int newfile = 0; char *cp, *cq; off_t mstats[2], tstats[2]; int compressed = 0; enum protocol prot; int success = 1, last = 0; /*LINTED*/ msgvec = (int *)salloc((msgCount + 2) * sizeof *msgvec); if (sender_record) { for (cp = str; *cp && blankchar(*cp & 0377); cp++); f = (*cp != '\0'); } else { if ((file = snarf(str, &f, convert != SEND_TOFILE)) == NULL) return(1); } if (!f) { *msgvec = first(0, MMNORM); if (*msgvec == 0) { if (inhook) return 0; printf(catgets(catd, CATSET, 23, "No messages to %s.\n"), cmd); return(1); } msgvec[1] = 0; } if (f && getmsglist(str, msgvec, 0) < 0) return(1); if (*msgvec == 0) { if (inhook) return 0; printf("No applicable messages.\n"); return 1; } if (sender_record) { if ((cp = nameof(&message[*msgvec - 1], 0)) == NULL) { printf(catgets(catd, CATSET, 24, "Cannot determine message sender to %s.\n"), cmd); return 1; } for (cq = cp; *cq && *cq != '@'; cq++); *cq = '\0'; if (value("outfolder")) { file = salloc(strlen(cp) + 2); file[0] = '+'; strcpy(&file[1], cp); } else file = cp; } if ((file = expand(file)) == NULL) return(1); prot = which_protocol(file); if (prot != PROTO_IMAP) { if (access(file, 0) >= 0) { newfile = 0; disp = catgets(catd, CATSET, 25, "[Appended]"); } else { newfile = 1; disp = catgets(catd, CATSET, 26, "[New file]"); } } if ((obuf = convert == SEND_TOFILE ? Fopen(file, "a+") : Zopen(file, "a+", &compressed)) == NULL) { if ((obuf = convert == SEND_TOFILE ? Fopen(file, "wx") : Zopen(file, "wx", &compressed)) == NULL) { perror(file); return(1); } } else { if (compressed) { newfile = 0; disp = catgets(catd, CATSET, 25, "[Appended]"); } if (!newfile && fstat(fileno(obuf), &st) && S_ISREG(st.st_mode) && fseek(obuf, -2L, SEEK_END) == 0) { char buf[2]; int prependnl = 0; switch (fread(buf, sizeof *buf, 2, obuf)) { case 2: if (buf[1] != '\n') { prependnl = 1; break; } /*FALLTHRU*/ case 1: if (buf[0] != '\n') prependnl = 1; break; default: if (ferror(obuf)) { perror(file); return(1); } prependnl = 0; } fflush(obuf); if (prependnl) { putc('\n', obuf); fflush(obuf); } } } tstats[0] = tstats[1] = 0; imap_created_mailbox = 0; for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { mp = &message[*ip - 1]; if (prot == PROTO_IMAP && ignore[0].i_count == 0 && ignore[1].i_count == 0 && imap_thisaccount(file)) { if (imap_copy(mp, *ip, file) == STOP) goto ferr; mstats[0] = -1; mstats[1] = mp->m_xsize; } else if (send(mp, obuf, ignore, NULL, convert, mstats) < 0) { perror(file); goto ferr; } touch(mp); if (mark) mp->m_flag |= MSAVED; if (domove) { mp->m_flag |= MDELETED|MSAVED; last = *ip; } tstats[0] += mstats[0]; tstats[1] += mstats[1]; } fflush(obuf); if (ferror(obuf)) { perror(file); ferr: success = 0; } if (Fclose(obuf) != 0) success = 0; if (success) { if (prot == PROTO_IMAP || prot == PROTO_MAILDIR) disp = prot == PROTO_IMAP && disconnected(file) ? "[Queued]" : imap_created_mailbox ? "[New file]" : "[Appended]"; printf("\"%s\" %s ", file, disp); if (tstats[0] >= 0) printf("%lu", (long)tstats[0]); else printf(catgets(catd, CATSET, 27, "binary")); printf("/%lu\n", (long)tstats[1]); } else if (mark) { for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { mp = &message[*ip - 1]; mp->m_flag &= ~MSAVED; } } else if (domove) { for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { mp = &message[*ip - 1]; mp->m_flag &= ~(MSAVED|MDELETED); } } if (domove && last && success) { setdot(&message[last-1]); last = first(0, MDELETED); setdot(&message[last ? last-1 : 0]); } return(success == 0); } /* * Write the indicated messages at the end of the passed * file name, minus header and trailing blank line. * This is the MIME save function. */ int cwrite(void *v) { char *str = v; return save1(str, 0, "write", allignore, SEND_TOFILE, 0, 0); } /* * Snarf the file from the end of the command line and * return a pointer to it. If there is no file attached, * return the mbox file. Put a null in front of the file * name so that the message list processing won't see it, * unless the file name is the only thing on the line, in * which case, return 0 in the reference flag variable. */ static char * snarf(char *linebuf, int *flag, int usembox) { char *cp; *flag = 1; if ((cp = laststring(linebuf, flag, 0)) == NULL) { if (usembox) { *flag = 0; return expand("&"); } else { printf(catgets(catd, CATSET, 28, "No file specified.\n")); return NULL; } } return(cp); } /* * Delete messages. */ int delete(void *v) { int *msgvec = v; delm(msgvec); return 0; } /* * Delete messages, then type the new dot. */ int deltype(void *v) { int *msgvec = v; int list[2]; int lastdot; lastdot = dot - &message[0] + 1; if (delm(msgvec) >= 0) { list[0] = dot - &message[0] + 1; if (list[0] > lastdot) { touch(dot); list[1] = 0; return(type(list)); } printf(catgets(catd, CATSET, 29, "At EOF\n")); } else printf(catgets(catd, CATSET, 30, "No more messages\n")); return(0); } /* * Delete the indicated messages. * Set dot to some nice place afterwards. * Internal interface. */ static int delm(int *msgvec) { struct message *mp; int *ip; int last; last = 0; for (ip = msgvec; *ip != 0; ip++) { mp = &message[*ip - 1]; touch(mp); mp->m_flag |= MDELETED|MTOUCH; mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX); last = *ip; } if (last != 0) { setdot(&message[last-1]); last = first(0, MDELETED); if (last != 0) { setdot(&message[last-1]); return(0); } else { setdot(&message[0]); return(-1); } } /* * Following can't happen -- it keeps lint happy */ return(-1); } /* * Undelete the indicated messages. */ int undeletecmd(void *v) { int *msgvec = v; struct message *mp; int *ip; for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { mp = &message[*ip - 1]; touch(mp); setdot(mp); if (mp->m_flag & (MDELETED|MSAVED)) mp->m_flag &= ~(MDELETED|MSAVED); else mp->m_flag &= ~MDELETED; if (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) imap_undelete(mp, *ip); } return 0; } #ifdef DEBUG_COMMANDS /* * Interactively dump core on "core" */ /*ARGSUSED*/ int core(void *v) { int pid; #ifdef WCOREDUMP extern int wait_status; #endif switch (pid = fork()) { case -1: perror("fork"); return(1); case 0: abort(); _exit(1); } printf(catgets(catd, CATSET, 31, "Okie dokie")); fflush(stdout); wait_child(pid); #ifdef WCOREDUMP if (WCOREDUMP(wait_status)) printf(catgets(catd, CATSET, 32, " -- Core dumped.\n")); else printf(catgets(catd, CATSET, 33, " -- Can't dump core.\n")); #endif return 0; } /* * Clobber as many bytes of stack as the user requests. */ int clobber(void *v) { char **argv = v; int times; if (argv[0] == 0) times = 1; else times = (atoi(argv[0]) + 511) / 512; clob1(times); return 0; } /* * Clobber the stack. */ static void clob1(int n) { char buf[512]; char *cp; if (n <= 0) return; for (cp = buf; cp < &buf[512]; *cp++ = (char)0xFF) ; clob1(n - 1); } #endif /* DEBUG_COMMANDS */ /* * Add the given header fields to the retained list. * If no arguments, print the current list of retained fields. */ int retfield(void *v) { char **list = v; return ignore1(list, ignore + 1, "retained"); } /* * Add the given header fields to the ignored list. * If no arguments, print the current list of ignored fields. */ int igfield(void *v) { char **list = v; return ignore1(list, ignore, "ignored"); } int saveretfield(void *v) { char **list = v; return ignore1(list, saveignore + 1, "retained"); } int saveigfield(void *v) { char **list = v; return ignore1(list, saveignore, "ignored"); } int fwdretfield(void *v) { char **list = v; return ignore1(list, fwdignore + 1, "retained"); } int fwdigfield(void *v) { char **list = v; return ignore1(list, fwdignore, "ignored"); } static int ignore1(char **list, struct ignoretab *tab, char *which) { int h; struct ignore *igp; char **ap; if (*list == NULL) return igshow(tab, which); for (ap = list; *ap != 0; ap++) { char *field; size_t sz; sz = strlen(*ap); field = ac_alloc(sz + 1); i_strcpy(field, *ap, sz + 1); field[sz]='\0'; if (member(field, tab)) { ac_free(field); continue; } h = hash(field); igp = (struct ignore *)scalloc(1, sizeof (struct ignore)); igp->i_field = smalloc(strlen(field) + 1); strcpy(igp->i_field, field); igp->i_link = tab->i_head[h]; tab->i_head[h] = igp; tab->i_count++; ac_free(field); } return 0; } /* * Print out all currently retained fields. */ static int igshow(struct ignoretab *tab, char *which) { int h; struct ignore *igp; char **ap, **ring; if (tab->i_count == 0) { printf(catgets(catd, CATSET, 34, "No fields currently being %s.\n"), which); return 0; } /*LINTED*/ ring = (char **)salloc((tab->i_count + 1) * sizeof (char *)); ap = ring; for (h = 0; h < HSHSIZE; h++) for (igp = tab->i_head[h]; igp != 0; igp = igp->i_link) *ap++ = igp->i_field; *ap = 0; qsort(ring, tab->i_count, sizeof (char *), igcomp); for (ap = ring; *ap != 0; ap++) printf("%s\n", *ap); return 0; } /* * Compare two names for sorting ignored field list. */ static int igcomp(const void *l, const void *r) { return (strcmp(*(char **)l, *(char **)r)); } int unignore(void *v) { return unignore1((char **)v, ignore, "ignored"); } int unretain(void *v) { return unignore1((char **)v, ignore + 1, "retained"); } int unsaveignore(void *v) { return unignore1((char **)v, saveignore, "ignored"); } int unsaveretain(void *v) { return unignore1((char **)v, saveignore + 1, "retained"); } int unfwdignore(void *v) { return unignore1((char **)v, fwdignore, "ignored"); } int unfwdretain(void *v) { return unignore1((char **)v, fwdignore + 1, "retained"); } static void unignore_one(const char *name, struct ignoretab *tab) { struct ignore *ip, *iq = NULL; int h = hash(name); for (ip = tab->i_head[h]; ip; ip = ip->i_link) { if (asccasecmp(ip->i_field, name) == 0) { free(ip->i_field); if (iq != NULL) iq->i_link = ip->i_link; else tab->i_head[h] = ip->i_link; free(ip); tab->i_count--; break; } iq = ip; } } static int unignore1(char **list, struct ignoretab *tab, char *which) { if (tab->i_count == 0) { printf(catgets(catd, CATSET, 34, "No fields currently being %s.\n"), which); return 0; } while (*list) unignore_one(*list++, tab); return 0; } heirloom-mailx-12.5/cmd3.c000066400000000000000000001034111155563371200153710ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)cmd3.c 2.87 (gritter) 10/1/08"; #endif #endif /* not lint */ #include #include #include "rcv.h" #include "extern.h" #include #include /* * Mail -- a mail program * * Still more user commands. */ static int bangexp(char **str, size_t *size); static void make_ref_and_cs(struct message *mp, struct header *head); static int (*respond_or_Respond(int c))(int *, int); static int respond_internal(int *msgvec, int recipient_record); static char *reedit(char *subj); static char *fwdedit(char *subj); static void onpipe(int signo); static void asort(char **list); static int diction(const void *a, const void *b); static int file1(char *name); static int shellecho(const char *cp); static int Respond_internal(int *msgvec, int recipient_record); static int resend1(void *v, int add_resent); static void list_shortcuts(void); static enum okay delete_shortcut(const char *str); static float huge(void); /* * Process a shell escape by saving signals, ignoring signals, * and forking a sh -c */ int shell(void *v) { char *str = v; sighandler_type sigint = safe_signal(SIGINT, SIG_IGN); char *shell; char *cmd; size_t cmdsize; cmd = smalloc(cmdsize = strlen(str) + 1); strcpy(cmd, str); if (bangexp(&cmd, &cmdsize) < 0) return 1; if ((shell = value("SHELL")) == NULL) shell = SHELL; run_command(shell, 0, -1, -1, "-c", cmd, NULL); safe_signal(SIGINT, sigint); printf("!\n"); free(cmd); return 0; } /* * Fork an interactive shell. */ /*ARGSUSED*/ int dosh(void *v) { sighandler_type sigint = safe_signal(SIGINT, SIG_IGN); char *shell; if ((shell = value("SHELL")) == NULL) shell = SHELL; run_command(shell, 0, -1, -1, NULL, NULL, NULL); safe_signal(SIGINT, sigint); putchar('\n'); return 0; } /* * Expand the shell escape by expanding unescaped !'s into the * last issued command where possible. */ static char *lastbang; static size_t lastbangsize; static int bangexp(char **str, size_t *size) { char *bangbuf; int changed = 0; int dobang = value("bang") != NULL; size_t sz, i, j, bangbufsize; bangbuf = smalloc(bangbufsize = *size); i = j = 0; while ((*str)[i]) { if (dobang) { if ((*str)[i] == '!') { sz = strlen(lastbang); bangbuf = srealloc(bangbuf, bangbufsize += sz); changed++; strcpy(&bangbuf[j], lastbang); j += sz; i++; continue; } } if ((*str)[i] == '\\' && (*str)[i + 1] == '!') { bangbuf[j++] = '!'; i += 2; changed++; } bangbuf[j++] = (*str)[i++]; } bangbuf[j] = '\0'; if (changed) { printf("!%s\n", bangbuf); fflush(stdout); } sz = j; if (sz >= *size) *str = srealloc(*str, *size = sz + 1); strcpy(*str, bangbuf); if (sz >= lastbangsize) lastbang = srealloc(lastbang, lastbangsize = sz + 1); strcpy(lastbang, bangbuf); free(bangbuf); return(0); } /*ARGSUSED*/ int help(void *v) { const char *helptext = " %s commands\n\ type type messages\n\ next goto and type next message\n\ from give head lines of messages\n\ headers print out active message headers\n\ delete delete messages\n\ undelete undelete messages\n\ save folder append messages to folder and mark as saved\n\ copy folder append messages to folder without marking them\n\ write file append message texts to file, save attachments\n\ preserve keep incoming messages in mailbox even if saved\n\ Reply reply to message senders\n\ reply reply to message senders and all recipients\n\ mail addresses mail to specific recipients\n\ file folder change to another folder\n\ quit quit and apply changes to folder\n\ xit quit and discard changes made to folder\n\ ! shell escape\n\ cd chdir to directory or home if none given\n\ list list names of all available commands\n\ \n\ A consists of integers, ranges of same, or other criteria\n\ separated by spaces. If omitted, %s uses the last message typed.\n"; fprintf(stdout, helptext, progname, progname); return(0); } /* * Change user's working directory. */ int schdir(void *v) { char **arglist = v; char *cp; if (*arglist == NULL) cp = homedir; else if ((cp = expand(*arglist)) == NULL) return(1); if (chdir(cp) < 0) { perror(cp); return(1); } return 0; } static void make_ref_and_cs(struct message *mp, struct header *head) { char *oldref, *oldmsgid, *newref, *cp; size_t reflen; unsigned i; struct name *n; oldref = hfield("references", mp); oldmsgid = hfield("message-id", mp); if (oldmsgid == NULL || *oldmsgid == '\0') { head->h_ref = NULL; return; } reflen = 1; if (oldref) reflen += strlen(oldref) + 2; if (oldmsgid) reflen += strlen(oldmsgid); newref = ac_alloc(reflen); if (oldref) { strcpy(newref, oldref); if (oldmsgid) { strcat(newref, ", "); strcat(newref, oldmsgid); } } else if (oldmsgid) strcpy(newref, oldmsgid); n = extract(newref, GREF); ac_free(newref); /* * Limit the references to 21 entries. */ while (n->n_flink != NULL) n = n->n_flink; for (i = 1; i < 21; i++) { if (n->n_blink != NULL) n = n->n_blink; else break; } n->n_blink = NULL; head->h_ref = n; if (value("reply-in-same-charset") != NULL && (cp = hfield("content-type", mp)) != NULL) head->h_charset = mime_getparam("charset", cp); } static int (*respond_or_Respond(int c))(int *, int) { int opt = 0; opt += (value("Replyall") != NULL); opt += (value("flipr") != NULL); return ((opt == 1) ^ (c == 'R')) ? Respond_internal : respond_internal; } int respond(void *v) { return (respond_or_Respond('r'))((int *)v, 0); } int respondall(void *v) { return respond_internal((int *)v, 0); } int respondsender(void *v) { return Respond_internal((int *)v, 0); } int followup(void *v) { return (respond_or_Respond('r'))((int *)v, 1); } int followupall(void *v) { return respond_internal((int *)v, 1); } int followupsender(void *v) { return Respond_internal((int *)v, 1); } /* * Reply to a list of messages. Extract each name from the * message header and send them off to mail1() */ static int respond_internal(int *msgvec, int recipient_record) { int Eflag; struct message *mp; char *cp, *rcv; enum gfield gf = value("fullnames") ? GFULL : GSKIN; struct name *np = NULL; struct header head; memset(&head, 0, sizeof head); if (msgvec[1] != 0) { printf(catgets(catd, CATSET, 37, "Sorry, can't reply to multiple messages at once\n")); return(1); } mp = &message[msgvec[0] - 1]; touch(mp); setdot(mp); if ((rcv = hfield("reply-to", mp)) == NULL) if ((rcv = hfield("from", mp)) == NULL) rcv = nameof(mp, 1); if (rcv != NULL) np = sextract(rcv, GTO|gf); if ((cp = hfield("to", mp)) != NULL) np = cat(np, sextract(cp, GTO|gf)); np = elide(np); /* * Delete my name from the reply list, * and with it, all my alternate names. */ np = delete_alternates(np); if (np == NULL) np = sextract(rcv, GTO|gf); head.h_to = np; if ((head.h_subject = hfield("subject", mp)) == NULL) head.h_subject = hfield("subj", mp); head.h_subject = reedit(head.h_subject); if ((cp = hfield("cc", mp)) != NULL) { np = elide(sextract(cp, GCC|gf)); np = delete_alternates(np); head.h_cc = np; } make_ref_and_cs(mp, &head); Eflag = value("skipemptybody") != NULL; if (mail1(&head, 1, mp, NULL, recipient_record, 0, 0, Eflag) == OKAY && value("markanswered") && (mp->m_flag & MANSWERED) == 0) mp->m_flag |= MANSWER|MANSWERED; return(0); } /* * Modify the subject we are replying to to begin with Re: if * it does not already. */ static char * reedit(char *subj) { char *newsubj; struct str in, out; if (subj == NULL || *subj == '\0') return NULL; in.s = subj; in.l = strlen(subj); mime_fromhdr(&in, &out, TD_ISPR|TD_ICONV); if ((out.s[0] == 'r' || out.s[0] == 'R') && (out.s[1] == 'e' || out.s[1] == 'E') && out.s[2] == ':') return out.s; newsubj = salloc(out.l + 5); strcpy(newsubj, "Re: "); strcpy(newsubj + 4, out.s); return newsubj; } /* * Forward a message to a new recipient, in the sense of RFC 2822. */ static int forward1(char *str, int recipient_record) { int Eflag; int *msgvec, f; char *recipient; struct message *mp; struct header head; int forward_as_attachment; forward_as_attachment = value("forward-as-attachment") != NULL; msgvec = salloc((msgCount + 2) * sizeof *msgvec); if ((recipient = laststring(str, &f, 0)) == NULL) { puts(catgets(catd, CATSET, 47, "No recipient specified.")); return 1; } if (!f) { *msgvec = first(0, MMNORM); if (*msgvec == 0) { if (inhook) return 0; printf("No messages to forward.\n"); return 1; } msgvec[1] = 0; } if (f && getmsglist(str, msgvec, 0) < 0) return 1; if (*msgvec == 0) { if (inhook) return 0; printf("No applicable messages.\n"); return 1; } if (msgvec[1] != 0) { printf("Cannot forward multiple messages at once\n"); return 1; } memset(&head, 0, sizeof head); if ((head.h_to = sextract(recipient, GTO | (value("fullnames") ? GFULL : GSKIN))) == NULL) return 1; mp = &message[*msgvec - 1]; if (forward_as_attachment) { head.h_attach = csalloc(1, sizeof *head.h_attach); head.h_attach->a_msgno = *msgvec; } else { touch(mp); setdot(mp); } if ((head.h_subject = hfield("subject", mp)) == NULL) head.h_subject = hfield("subj", mp); head.h_subject = fwdedit(head.h_subject); Eflag = value("skipemptybody") != NULL; mail1(&head, 1, forward_as_attachment ? NULL : mp, NULL, recipient_record, 1, 0, Eflag); return 0; } /* * Modify the subject we are replying to to begin with Fwd:. */ static char * fwdedit(char *subj) { char *newsubj; struct str in, out; if (subj == NULL || *subj == '\0') return NULL; in.s = subj; in.l = strlen(subj); mime_fromhdr(&in, &out, TD_ISPR|TD_ICONV); newsubj = salloc(strlen(out.s) + 6); strcpy(newsubj, "Fwd: "); strcpy(&newsubj[5], out.s); free(out.s); return newsubj; } /* * The 'forward' command. */ int forwardcmd(void *v) { return forward1(v, 0); } /* * Similar to forward, saving the message in a file named after the * first recipient. */ int Forwardcmd(void *v) { return forward1(v, 1); } /* * Preserve the named messages, so that they will be sent * back to the system mailbox. */ int preserve(void *v) { int *msgvec = v; struct message *mp; int *ip, mesg; if (edit) { printf(catgets(catd, CATSET, 39, "Cannot \"preserve\" in edit mode\n")); return(1); } for (ip = msgvec; *ip != 0; ip++) { mesg = *ip; mp = &message[mesg-1]; mp->m_flag |= MPRESERVE; mp->m_flag &= ~MBOX; setdot(mp); /* * This is now Austin Group Request XCU #20. */ did_print_dot = 1; } return(0); } /* * Mark all given messages as unread. */ int unread(void *v) { int *msgvec = v; int *ip; for (ip = msgvec; *ip != 0; ip++) { setdot(&message[*ip-1]); dot->m_flag &= ~(MREAD|MTOUCH); dot->m_flag |= MSTATUS; if (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) imap_unread(&message[*ip-1], *ip); /* * The "unread" command is not part of POSIX mailx. */ did_print_dot = 1; } return(0); } /* * Mark all given messages as read. */ int seen(void *v) { int *msgvec = v; int *ip; for (ip = msgvec; *ip; ip++) { setdot(&message[*ip-1]); touch(&message[*ip-1]); } return 0; } /* * Print the size of each message. */ int messize(void *v) { int *msgvec = v; struct message *mp; int *ip, mesg; for (ip = msgvec; *ip != 0; ip++) { mesg = *ip; mp = &message[mesg-1]; printf("%d: ", mesg); if (mp->m_xlines > 0) printf("%ld", mp->m_xlines); else putchar(' '); printf("/%lu\n", (unsigned long)mp->m_xsize); } return(0); } /* * Quit quickly. If we are sourcing, just pop the input level * by returning an error. */ /*ARGSUSED*/ int rexit(void *v) { if (sourcing) return(1); exit(0); /*NOTREACHED*/ } static sigjmp_buf pipejmp; /*ARGSUSED*/ static void onpipe(int signo) { siglongjmp(pipejmp, 1); } /* * Set or display a variable value. Syntax is similar to that * of sh. */ int set(void *v) { char **arglist = v; struct var *vp; char *cp, *cp2; char **ap, **p; int errs, h, s; FILE *obuf = stdout; int bsdset = value("bsdcompat") != NULL || value("bsdset") != NULL; (void)&cp; (void)≈ (void)&obuf; (void)&bsdset; if (*arglist == NULL) { for (h = 0, s = 1; h < HSHSIZE; h++) for (vp = variables[h]; vp != NULL; vp = vp->v_link) s++; /*LINTED*/ ap = (char **)salloc(s * sizeof *ap); for (h = 0, p = ap; h < HSHSIZE; h++) for (vp = variables[h]; vp != NULL; vp = vp->v_link) *p++ = vp->v_name; *p = NULL; asort(ap); if (is_a_tty[0] && is_a_tty[1] && (cp = value("crt")) != NULL) { if (s > (*cp == '\0' ? screensize() : atoi(cp)) + 3) { cp = get_pager(); if (sigsetjmp(pipejmp, 1)) goto endpipe; if ((obuf = Popen(cp, "w", NULL, 1)) == NULL) { perror(cp); obuf = stdout; } else safe_signal(SIGPIPE, onpipe); } } for (p = ap; *p != NULL; p++) { if (bsdset) fprintf(obuf, "%s\t%s\n", *p, value(*p)); else { if ((cp = value(*p)) != NULL && *cp) fprintf(obuf, "%s=\"%s\"\n", *p, value(*p)); else fprintf(obuf, "%s\n", *p); } } endpipe: if (obuf != stdout) { safe_signal(SIGPIPE, SIG_IGN); Pclose(obuf); safe_signal(SIGPIPE, dflpipe); } return(0); } errs = 0; for (ap = arglist; *ap != NULL; ap++) { char *varbuf; varbuf = ac_alloc(strlen(*ap) + 1); cp = *ap; cp2 = varbuf; while (*cp != '=' && *cp != '\0') *cp2++ = *cp++; *cp2 = '\0'; if (*cp == '\0') cp = ""; else cp++; if (equal(varbuf, "")) { printf(catgets(catd, CATSET, 41, "Non-null variable name required\n")); errs++; ac_free(varbuf); continue; } if (varbuf[0] == 'n' && varbuf[1] == 'o') errs += unset_internal(&varbuf[2]); else assign(varbuf, cp); ac_free(varbuf); } return(errs); } /* * Unset a bunch of variable values. */ int unset(void *v) { int errs; char **ap; errs = 0; for (ap = (char **)v; *ap != NULL; ap++) errs += unset_internal(*ap); return(errs); } /* * Put add users to a group. */ int group(void *v) { char **argv = v; struct grouphead *gh; struct group *gp; int h; int s; char **ap, *gname, **p; if (*argv == NULL) { for (h = 0, s = 1; h < HSHSIZE; h++) for (gh = groups[h]; gh != NULL; gh = gh->g_link) s++; /*LINTED*/ ap = (char **)salloc(s * sizeof *ap); for (h = 0, p = ap; h < HSHSIZE; h++) for (gh = groups[h]; gh != NULL; gh = gh->g_link) *p++ = gh->g_name; *p = NULL; asort(ap); for (p = ap; *p != NULL; p++) printgroup(*p); return(0); } if (argv[1] == NULL) { printgroup(*argv); return(0); } gname = *argv; h = hash(gname); if ((gh = findgroup(gname)) == NULL) { gh = (struct grouphead *)scalloc(1, sizeof *gh); gh->g_name = vcopy(gname); gh->g_list = NULL; gh->g_link = groups[h]; groups[h] = gh; } /* * Insert names from the command list into the group. * Who cares if there are duplicates? They get tossed * later anyway. */ for (ap = argv+1; *ap != NULL; ap++) { gp = (struct group *)scalloc(1, sizeof *gp); gp->ge_name = vcopy(*ap); gp->ge_link = gh->g_list; gh->g_list = gp; } return(0); } /* * Delete the passed groups. */ int ungroup(void *v) { char **argv = v; if (*argv == NULL) { printf(catgets(catd, CATSET, 209, "Must specify alias or group to remove\n")); return 1; } do remove_group(*argv); while (*++argv != NULL); return 0; } /* * Sort the passed string vecotor into ascending dictionary * order. */ static void asort(char **list) { char **ap; for (ap = list; *ap != NULL; ap++) ; if (ap-list < 2) return; qsort(list, ap-list, sizeof(*list), diction); } /* * Do a dictionary order comparison of the arguments from * qsort. */ static int diction(const void *a, const void *b) { return(strcmp(*(char **)a, *(char **)b)); } /* * Change to another file. With no argument, print information about * the current file. */ int cfile(void *v) { char **argv = v; if (argv[0] == NULL) { newfileinfo(); return 0; } strncpy(mboxname, expand("&"), sizeof mboxname)[sizeof mboxname-1]='\0'; return file1(*argv); } static int file1(char *name) { int i; if (inhook) { fprintf(stderr, "Cannot change folder from within a hook.\n"); return 1; } i = setfile(name, 0); if (i < 0) return 1; callhook(mailname, 0); if (i > 0 && value("emptystart") == NULL) return 1; announce(value("bsdcompat") != NULL || value("bsdannounce") != NULL); return 0; } static int shellecho(const char *cp) { int cflag = 0, n; char c; while (*cp) { if (*cp == '\\') { switch (*++cp) { case '\0': return cflag; case 'a': putchar('\a'); break; case 'b': putchar('\b'); break; case 'c': cflag = 1; break; case 'f': putchar('\f'); break; case 'n': putchar('\n'); break; case 'r': putchar('\r'); break; case 't': putchar('\t'); break; case 'v': putchar('\v'); break; default: putchar(*cp&0377); break; case '0': c = 0; n = 3; while (n-- && octalchar(cp[1]&0377)) { c <<= 3; c |= cp[1] - '0'; cp++; } putchar(c); } } else putchar(*cp & 0377); cp++; } return cflag; } /* * Expand file names like echo */ int echo(void *v) { char **argv = v; char **ap; char *cp; int cflag = 0; for (ap = argv; *ap != NULL; ap++) { cp = *ap; if ((cp = expand(cp)) != NULL) { if (ap != argv) putchar(' '); cflag |= shellecho(cp); } } if (!cflag) putchar('\n'); return 0; } int Respond(void *v) { return (respond_or_Respond('R'))((int *)v, 0); } int Followup(void *v) { return (respond_or_Respond('R'))((int *)v, 1); } /* * Reply to a series of messages by simply mailing to the senders * and not messing around with the To: and Cc: lists as in normal * reply. */ static int Respond_internal(int *msgvec, int recipient_record) { int Eflag; struct header head; struct message *mp; enum gfield gf = value("fullnames") ? GFULL : GSKIN; int *ap; char *cp; memset(&head, 0, sizeof head); for (ap = msgvec; *ap != 0; ap++) { mp = &message[*ap - 1]; touch(mp); setdot(mp); if ((cp = hfield("reply-to", mp)) == NULL) if ((cp = hfield("from", mp)) == NULL) cp = nameof(mp, 2); head.h_to = cat(head.h_to, sextract(cp, GTO|gf)); } if (head.h_to == NULL) return 0; mp = &message[msgvec[0] - 1]; if ((head.h_subject = hfield("subject", mp)) == NULL) head.h_subject = hfield("subj", mp); head.h_subject = reedit(head.h_subject); make_ref_and_cs(mp, &head); Eflag = value("skipemptybody") != NULL; if (mail1(&head, 1, mp, NULL, recipient_record, 0, 0, Eflag) == OKAY && value("markanswered") && (mp->m_flag & MANSWERED) == 0) mp->m_flag |= MANSWER|MANSWERED; return 0; } /* * Conditional commands. These allow one to parameterize one's * .mailrc and do some things if sending, others if receiving. */ int ifcmd(void *v) { char **argv = v; char *cp; if (cond != CANY) { printf(catgets(catd, CATSET, 42, "Illegal nested \"if\"\n")); return(1); } cond = CANY; cp = argv[0]; switch (*cp) { case 'r': case 'R': cond = CRCV; break; case 's': case 'S': cond = CSEND; break; case 't': case 'T': cond = CTERM; break; default: printf(catgets(catd, CATSET, 43, "Unrecognized if-keyword: \"%s\"\n"), cp); return(1); } return(0); } /* * Implement 'else'. This is pretty simple -- we just * flip over the conditional flag. */ /*ARGSUSED*/ int elsecmd(void *v) { switch (cond) { case CANY: printf(catgets(catd, CATSET, 44, "\"Else\" without matching \"if\"\n")); return(1); case CSEND: cond = CRCV; break; case CRCV: cond = CSEND; break; case CTERM: cond = CNONTERM; break; default: printf(catgets(catd, CATSET, 45, "Mail's idea of conditions is screwed up\n")); cond = CANY; break; } return(0); } /* * End of if statement. Just set cond back to anything. */ /*ARGSUSED*/ int endifcmd(void *v) { if (cond == CANY) { printf(catgets(catd, CATSET, 46, "\"Endif\" without matching \"if\"\n")); return(1); } cond = CANY; return(0); } /* * Set the list of alternate names. */ int alternates(void *v) { char **namelist = v; int c; char **ap, **ap2, *cp; c = argcount(namelist) + 1; if (c == 1) { if (altnames == 0) return(0); for (ap = altnames; *ap; ap++) printf("%s ", *ap); printf("\n"); return(0); } if (altnames != 0) free(altnames); altnames = scalloc(c, sizeof (char *)); for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) { cp = scalloc(strlen(*ap) + 1, sizeof (char)); strcpy(cp, *ap); *ap2 = cp; } *ap2 = 0; return(0); } /* * Do the real work of resending. */ static int resend1(void *v, int add_resent) { char *name, *str; struct name *to; struct name *sn; int f, *ip, *msgvec; str = (char *)v; /*LINTED*/ msgvec = (int *)salloc((msgCount + 2) * sizeof *msgvec); name = laststring(str, &f, 1); if (name == NULL) { puts(catgets(catd, CATSET, 47, "No recipient specified.")); return 1; } if (!f) { *msgvec = first(0, MMNORM); if (*msgvec == 0) { if (inhook) return 0; puts(catgets(catd, CATSET, 48, "No applicable messages.")); return 1; } msgvec[1] = 0; } else if (getmsglist(str, msgvec, 0) < 0) return 1; if (*msgvec == 0) { if (inhook) return 0; printf("No applicable messages.\n"); return 1; } sn = nalloc(name, GTO); to = usermap(sn); for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) { if (resend_msg(&message[*ip - 1], to, add_resent) != OKAY) return 1; } return 0; } /* * Resend a message list to a third person. */ int resendcmd(void *v) { return resend1(v, 1); } /* * Resend a message list to a third person without adding headers. */ int Resendcmd(void *v) { return resend1(v, 0); } /* * 'newmail' or 'inc' command: Check for new mail without writing old * mail back. */ /*ARGSUSED*/ int newmail(void *v) { int val = 1, mdot; if ((mb.mb_type != MB_IMAP || imap_newmail(1)) && (val = setfile(mailname, 1)) == 0) { mdot = getmdot(1); setdot(&message[mdot - 1]); } return val; } static void list_shortcuts(void) { struct shortcut *s; for (s = shortcuts; s; s = s->sh_next) printf("%s=%s\n", s->sh_short, s->sh_long); } int shortcut(void *v) { char **args = (char **)v; struct shortcut *s; if (args[0] == NULL) { list_shortcuts(); return 0; } if (args[1] == NULL) { fprintf(stderr, catgets(catd, CATSET, 220, "expansion name for shortcut missing\n")); return 1; } if (args[2] != NULL) { fprintf(stderr, catgets(catd, CATSET, 221, "too many arguments\n")); return 1; } if ((s = get_shortcut(args[0])) != NULL) { free(s->sh_long); s->sh_long = sstrdup(args[1]); } else { s = scalloc(1, sizeof *s); s->sh_short = sstrdup(args[0]); s->sh_long = sstrdup(args[1]); s->sh_next = shortcuts; shortcuts = s; } return 0; } struct shortcut * get_shortcut(const char *str) { struct shortcut *s; for (s = shortcuts; s; s = s->sh_next) if (strcmp(str, s->sh_short) == 0) break; return s; } static enum okay delete_shortcut(const char *str) { struct shortcut *sp, *sq; for (sp = shortcuts, sq = NULL; sp; sq = sp, sp = sp->sh_next) { if (strcmp(sp->sh_short, str) == 0) { free(sp->sh_short); free(sp->sh_long); if (sq) sq->sh_next = sp->sh_next; if (sp == shortcuts) shortcuts = sp->sh_next; free(sp); return OKAY; } } return STOP; } int unshortcut(void *v) { char **args = (char **)v; int errs = 0; if (args[0] == NULL) { fprintf(stderr, catgets(catd, CATSET, 222, "need shortcut names to remove\n")); return 1; } while (*args != NULL) { if (delete_shortcut(*args) != OKAY) { errs = 1; fprintf(stderr, catgets(catd, CATSET, 223, "%s: no such shortcut\n"), *args); } args++; } return errs; } struct oldaccount { struct oldaccount *ac_next; /* next account in list */ char *ac_name; /* name of account */ char **ac_vars; /* variables to set */ }; static struct oldaccount *oldaccounts; struct oldaccount * get_oldaccount(const char *name) { struct oldaccount *a; for (a = oldaccounts; a; a = a->ac_next) if (a->ac_name && strcmp(name, a->ac_name) == 0) break; return a; } int account(void *v) { char **args = (char **)v; struct oldaccount *a; char *cp; int i, mc, oqf, nqf; FILE *fp = stdout; if (args[0] == NULL) { if ((fp = Ftemp(&cp, "Ra", "w+", 0600, 1)) == NULL) { perror("tmpfile"); return 1; } rm(cp); Ftfree(&cp); mc = listaccounts(fp); for (a = oldaccounts; a; a = a->ac_next) if (a->ac_name) { if (mc++) fputc('\n', fp); fprintf(fp, "%s:\n", a->ac_name); for (i = 0; a->ac_vars[i]; i++) fprintf(fp, "\t%s\n", a->ac_vars[i]); } if (mc) try_pager(fp); Fclose(fp); return 0; } if (args[1] && args[1][0] == '{' && args[1][1] == '\0') { if (args[2] != NULL) { fprintf(stderr, "Syntax is: account {\n"); return 1; } if ((a = get_oldaccount(args[0])) != NULL) a->ac_name = NULL; return define1(args[0], 1); } strncpy(mboxname, expand("&"), sizeof mboxname)[sizeof mboxname-1]='\0'; oqf = savequitflags(); if ((a = get_oldaccount(args[0])) == NULL) { if (args[1]) { a = scalloc(1, sizeof *a); a->ac_next = oldaccounts; oldaccounts = a; } else { if ((i = callaccount(args[0])) != CBAD) goto setf; printf("Account %s does not exist.\n", args[0]); return 1; } } if (args[1]) { delaccount(args[0]); a->ac_name = sstrdup(args[0]); for (i = 1; args[i]; i++); a->ac_vars = scalloc(i, sizeof *a->ac_vars); for (i = 0; args[i+1]; i++) a->ac_vars[i] = sstrdup(args[i+1]); } else { unset_allow_undefined = 1; set(a->ac_vars); unset_allow_undefined = 0; setf: if (!starting) { nqf = savequitflags(); restorequitflags(oqf); i = file1("%"); restorequitflags(nqf); return i; } } return 0; } int cflag(void *v) { struct message *m; int *msgvec = v; int *ip; for (ip = msgvec; *ip != 0; ip++) { m = &message[*ip-1]; setdot(m); if ((m->m_flag & (MFLAG|MFLAGGED)) == 0) m->m_flag |= MFLAG|MFLAGGED; } return 0; } int cunflag(void *v) { struct message *m; int *msgvec = v; int *ip; for (ip = msgvec; *ip != 0; ip++) { m = &message[*ip-1]; setdot(m); if (m->m_flag & (MFLAG|MFLAGGED)) { m->m_flag &= ~(MFLAG|MFLAGGED); m->m_flag |= MUNFLAG; } } return 0; } int canswered(void *v) { struct message *m; int *msgvec = v; int *ip; for (ip = msgvec; *ip != 0; ip++) { m = &message[*ip-1]; setdot(m); if ((m->m_flag & (MANSWER|MANSWERED)) == 0) m->m_flag |= MANSWER|MANSWERED; } return 0; } int cunanswered(void *v) { struct message *m; int *msgvec = v; int *ip; for (ip = msgvec; *ip != 0; ip++) { m = &message[*ip-1]; setdot(m); if (m->m_flag & (MANSWER|MANSWERED)) { m->m_flag &= ~(MANSWER|MANSWERED); m->m_flag |= MUNANSWER; } } return 0; } int cdraft(void *v) { struct message *m; int *msgvec = v; int *ip; for (ip = msgvec; *ip != 0; ip++) { m = &message[*ip-1]; setdot(m); if ((m->m_flag & (MDRAFT|MDRAFTED)) == 0) m->m_flag |= MDRAFT|MDRAFTED; } return 0; } int cundraft(void *v) { struct message *m; int *msgvec = v; int *ip; for (ip = msgvec; *ip != 0; ip++) { m = &message[*ip-1]; setdot(m); if (m->m_flag & (MDRAFT|MDRAFTED)) { m->m_flag &= ~(MDRAFT|MDRAFTED); m->m_flag |= MUNDRAFT; } } return 0; } static float huge(void) { #if defined (_CRAY) /* * This is not perfect, but correct for machines with a 32-bit * IEEE float and a 32-bit unsigned long, and does at least not * produce SIGFPE on the Cray Y-MP. */ union { float f; unsigned long l; } u; u.l = 0xff800000; /* -inf */ return u.f; #elif defined (INFINITY) return -INFINITY; #elif defined (HUGE_VALF) return -HUGE_VALF; #elif defined (FLT_MAX) return -FLT_MAX; #else return -1e10; #endif } int ckill(void *v) { struct message *m; int *msgvec = v; int *ip; for (ip = msgvec; *ip != 0; ip++) { m = &message[*ip-1]; m->m_flag |= MKILL; m->m_score = huge(); } return 0; } int cunkill(void *v) { struct message *m; int *msgvec = v; int *ip; for (ip = msgvec; *ip != 0; ip++) { m = &message[*ip-1]; m->m_flag &= ~MKILL; m->m_score = 0; } return 0; } int cscore(void *v) { char *str = v; char *sscore, *xp; int f, *msgvec, *ip; double nscore; struct message *m; msgvec = salloc((msgCount+2) * sizeof *msgvec); if ((sscore = laststring(str, &f, 0)) == NULL) { fprintf(stderr, "No score given.\n"); return 1; } nscore = strtod(sscore, &xp); if (*xp) { fprintf(stderr, "Invalid score: \"%s\"\n", sscore); return 1; } if (nscore > FLT_MAX) nscore = FLT_MAX; else if (nscore < -FLT_MAX) nscore = -FLT_MAX; if (!f) { *msgvec = first(0, MMNORM); if (*msgvec == 0) { if (inhook) return 0; fprintf(stderr, "No messages to score.\n"); return 1; } msgvec[1] = 0; } else if (getmsglist(str, msgvec, 0) < 0) return 1; if (*msgvec == 0) { if (inhook) return 0; fprintf(stderr, "No applicable messages.\n"); return 1; } for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { m = &message[*ip-1]; if (m->m_score != huge()) { m->m_score += nscore; if (m->m_score < 0) m->m_flag |= MKILL; else if (m->m_score > 0) m->m_flag &= ~MKILL; if (m->m_score >= 0) setdot(m); } } return 0; } /*ARGSUSED*/ int cnoop(void *v) { switch (mb.mb_type) { case MB_IMAP: imap_noop(); break; case MB_POP3: pop3_noop(); break; default: break; } return 0; } int cremove(void *v) { char vb[LINESIZE]; char **args = v; char *name; int ec = 0; if (*args == NULL) { fprintf(stderr, "Syntax is: remove mailbox ...\n"); return 1; } do { if ((name = expand(*args)) == NULL) continue; if (strcmp(name, mailname) == 0) { fprintf(stderr, "Cannot remove current mailbox \"%s\".\n", name); ec |= 1; continue; } snprintf(vb, sizeof vb, "Remove \"%s\" (y/n) ? ", name); if (yorn(vb) == 0) continue; switch (which_protocol(name)) { case PROTO_FILE: if (unlink(name) < 0) { /* do not handle .gz .bz2 */ perror(name); ec |= 1; } break; case PROTO_POP3: fprintf(stderr, "Cannot remove POP3 mailbox \"%s\".\n", name); ec |= 1; break; case PROTO_IMAP: if (imap_remove(name) != OKAY) ec |= 1; break; case PROTO_MAILDIR: if (maildir_remove(name) != OKAY) ec |= 1; break; case PROTO_UNKNOWN: fprintf(stderr, "Unknown protocol in \"%s\". Not removed.\n", name); ec |= 1; } } while (*++args); return ec; } int crename(void *v) { char **args = v, *old, *new; enum protocol oldp, newp; int ec = 0; if (args[0] == NULL || args[1] == NULL || args[2] != NULL) { fprintf(stderr, "Syntax: rename old new\n"); return 1; } old = expand(args[0]); oldp = which_protocol(old); new = expand(args[1]); newp = which_protocol(new); if (strcmp(old, mailname) == 0 || strcmp(new, mailname) == 0) { fprintf(stderr, "Cannot rename current mailbox \"%s\".\n", old); return 1; } if ((oldp == PROTO_IMAP || newp == PROTO_IMAP) && oldp != newp) { fprintf(stderr, "Can only rename folders of same type.\n"); return 1; } if (newp == PROTO_POP3) goto nopop3; switch (oldp) { case PROTO_FILE: if (link(old, new) < 0) { switch (errno) { case EACCES: case EEXIST: case ENAMETOOLONG: case ENOENT: case ENOSPC: case EXDEV: perror(new); break; default: perror(old); } ec |= 1; } else if (unlink(old) < 0) { perror(old); ec |= 1; } break; case PROTO_MAILDIR: if (rename(old, new) < 0) { perror(old); ec |= 1; } break; case PROTO_POP3: nopop3: fprintf(stderr, "Cannot rename POP3 mailboxes.\n"); ec |= 1; break; case PROTO_IMAP: if (imap_rename(old, new) != OKAY) ec |= 1; break; case PROTO_UNKNOWN: fprintf(stderr, "Unknown protocol in \"%s\" and \"%s\". " "Not renamed.\n", old, new); ec |= 1; } return ec; } heirloom-mailx-12.5/cmdtab.c000066400000000000000000000220371155563371200160010ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)cmdtab.c 2.51 (gritter) 3/4/06"; #endif #endif /* not lint */ #include "rcv.h" #include "extern.h" /* * Mail -- a mail program * * Define all of the command names and bindings. */ const struct cmd cmdtab[] = { { "next", next, A|NDMLIST, 0, MMNDEL }, { "alias", group, M|RAWLIST, 0, 1000 }, { "print", type, A|MSGLIST, 0, MMNDEL }, { "type", type, A|MSGLIST, 0, MMNDEL }, { "Type", Type, A|MSGLIST, 0, MMNDEL }, { "Print", Type, A|MSGLIST, 0, MMNDEL }, { "visual", visual, A|I|MSGLIST, 0, MMNORM }, { "top", top, A|MSGLIST, 0, MMNDEL }, { "touch", stouch, A|W|MSGLIST, 0, MMNDEL }, { "preserve", preserve, A|W|MSGLIST, 0, MMNDEL }, { "delete", delete, A|W|P|MSGLIST, 0, MMNDEL }, { "dp", deltype, A|W|MSGLIST, 0, MMNDEL }, { "dt", deltype, A|W|MSGLIST, 0, MMNDEL }, { "undelete", undeletecmd, A|P|MSGLIST, MDELETED,MMNDEL }, { "unset", unset, M|RAWLIST, 1, 1000 }, { "mail", sendmail, R|M|I|STRLIST, 0, 0 }, { "Mail", Sendmail, R|M|I|STRLIST, 0, 0 }, { "mbox", mboxit, A|W|MSGLIST, 0, 0 }, { "more", more, A|MSGLIST, 0, MMNDEL }, { "page", more, A|MSGLIST, 0, MMNDEL }, { "More", More, A|MSGLIST, 0, MMNDEL }, { "Page", More, A|MSGLIST, 0, MMNDEL }, { "unread", unread, A|MSGLIST, 0, MMNDEL }, { "Unread", unread, A|MSGLIST, 0, MMNDEL }, { "new", unread, A|MSGLIST, 0, MMNDEL }, { "New", unread, A|MSGLIST, 0, MMNDEL }, { "!", shell, I|STRLIST, 0, 0 }, { "copy", copycmd, A|M|STRLIST, 0, 0 }, { "Copy", Copycmd, A|M|STRLIST, 0, 0 }, { "chdir", schdir, M|RAWLIST, 0, 1 }, { "cd", schdir, M|RAWLIST, 0, 1 }, { "save", save, A|STRLIST, 0, 0 }, { "Save", Save, A|STRLIST, 0, 0 }, { "source", source, M|RAWLIST, 1, 1 }, { "set", set, M|RAWLIST, 0, 1000 }, { "shell", dosh, I|NOLIST, 0, 0 }, { "version", pversion, M|NOLIST, 0, 0 }, { "group", group, M|RAWLIST, 0, 1000 }, { "ungroup", ungroup, M|RAWLIST, 0, 1000 }, { "unalias", ungroup, M|RAWLIST, 0, 1000 }, { "write", cwrite, A|STRLIST, 0, 0 }, { "from", from, A|MSGLIST, 0, MMNORM }, { "file", cfile, T|M|RAWLIST, 0, 1 }, { "followup", followup, A|R|I|MSGLIST, 0, MMNDEL }, { "followupall", followupall, A|R|I|MSGLIST, 0, MMNDEL }, { "followupsender", followupsender, A|R|I|MSGLIST, 0, MMNDEL }, { "folder", cfile, T|M|RAWLIST, 0, 1 }, { "folders", folders, T|M|RAWLIST, 0, 1 }, { "?", help, M|NOLIST, 0, 0 }, { "z", scroll, A|M|STRLIST, 0, 0 }, { "Z", Scroll, A|M|STRLIST, 0, 0 }, { "headers", headers, A|MSGLIST, 0, MMNDEL }, { "help", help, M|NOLIST, 0, 0 }, { "=", pdot, A|NOLIST, 0, 0 }, { "Reply", Respond, A|R|I|MSGLIST, 0, MMNDEL }, { "Respond", Respond, A|R|I|MSGLIST, 0, MMNDEL }, { "Followup", Followup, A|R|I|MSGLIST, 0, MMNDEL }, { "reply", respond, A|R|I|MSGLIST, 0, MMNDEL }, { "replyall", respondall, A|R|I|MSGLIST, 0, MMNDEL }, { "replysender", respondsender, A|R|I|MSGLIST, 0, MMNDEL }, { "respond", respond, A|R|I|MSGLIST, 0, MMNDEL }, { "respondall", respondall, A|R|I|MSGLIST, 0, MMNDEL }, { "respondsender", respondsender, A|R|I|MSGLIST,0, MMNDEL }, { "Resend", Resendcmd, A|R|STRLIST, 0, MMNDEL }, { "Redirect", Resendcmd, A|R|STRLIST, 0, MMNDEL }, { "resend", resendcmd, A|R|STRLIST, 0, MMNDEL }, { "redirect", resendcmd, A|R|STRLIST, 0, MMNDEL }, { "Forward", Forwardcmd, A|R|STRLIST, 0, MMNDEL }, { "Fwd", Forwardcmd, A|R|STRLIST, 0, MMNDEL }, { "forward", forwardcmd, A|R|STRLIST, 0, MMNDEL }, { "fwd", forwardcmd, A|R|STRLIST, 0, MMNDEL }, { "edit", editor, A|I|MSGLIST, 0, MMNORM }, { "echo", echo, M|ECHOLIST, 0, 1000 }, { "quit", quitcmd, NOLIST, 0, 0 }, { "list", pcmdlist, M|NOLIST, 0, 0 }, { "xit", rexit, M|NOLIST, 0, 0 }, { "exit", rexit, M|NOLIST, 0, 0 }, { "pipe", pipecmd, A|STRLIST, 0, MMNDEL }, { "|", pipecmd, A|STRLIST, 0, MMNDEL }, { "Pipe", Pipecmd, A|STRLIST, 0, MMNDEL }, { "size", messize, A|MSGLIST, 0, MMNDEL }, { "hold", preserve, A|W|MSGLIST, 0, MMNDEL }, { "if", ifcmd, F|M|RAWLIST, 1, 1 }, { "else", elsecmd, F|M|RAWLIST, 0, 0 }, { "endif", endifcmd, F|M|RAWLIST, 0, 0 }, { "alternates", alternates, M|RAWLIST, 0, 1000 }, { "ignore", igfield, M|RAWLIST, 0, 1000 }, { "discard", igfield, M|RAWLIST, 0, 1000 }, { "retain", retfield, M|RAWLIST, 0, 1000 }, { "saveignore", saveigfield, M|RAWLIST, 0, 1000 }, { "savediscard",saveigfield, M|RAWLIST, 0, 1000 }, { "saveretain", saveretfield, M|RAWLIST, 0, 1000 }, { "unignore", unignore, M|RAWLIST, 0, 1000 }, { "unretain", unretain, M|RAWLIST, 0, 1000 }, { "unsaveignore", unsaveignore, M|RAWLIST, 0, 1000 }, { "unsaveretain", unsaveretain, M|RAWLIST, 0, 1000 }, { "inc", newmail, A|T|NOLIST, 0, 0 }, { "newmail", newmail, A|T|NOLIST, 0, 0 }, { "shortcut", shortcut, M|RAWLIST, 0, 1000 }, { "unshortcut", unshortcut, M|RAWLIST, 0, 1000 }, { "imap", imap_imap, A|STRLIST, 0, 1000 }, { "account", account, M|RAWLIST, 0, 1000 }, { "thread", thread, A|MSGLIST, 0, 0 }, { "unthread", unthread, A|MSGLIST, 0, 0 }, { "online", cconnect, A|NOLIST, 0, 0 }, { "connect", cconnect, A|NOLIST, 0, 0 }, { "disconnect", cdisconnect, A|NDMLIST, 0, 0 }, { "sort", sort, A|RAWLIST, 0, 1 }, { "unsort", unthread, A|MSGLIST, 0, 0 }, { "cache", ccache, A|MSGLIST, 0, 0 }, { "flag", cflag, A|M|MSGLIST, 0, 0 }, { "unflag", cunflag, A|M|MSGLIST, 0, 0 }, { "answered", canswered, A|M|MSGLIST, 0, 0 }, { "unanswered", cunanswered, A|M|MSGLIST, 0, 0 }, { "draft", cdraft, A|M|MSGLIST, 0, 0 }, { "undraft", cundraft, A|M|MSGLIST, 0, 0 }, { "kill", ckill, A|M|MSGLIST, 0, 0 }, { "unkill", cunkill, A|M|MSGLIST, 0, 0 }, { "score", cscore, A|M|STRLIST, 0, 0 }, { "define", cdefine, M|RAWLIST, 0, 2 }, { "defines", cdefines, M|RAWLIST, 0, 0 }, { "undef", cundef, M|RAWLIST, 0, 1000 }, { "call", ccall, M|RAWLIST, 0, 1 }, { "move", cmove, A|M|STRLIST, 0, 0 }, { "mv", cmove, A|M|STRLIST, 0, 0 }, { "Move", cMove, A|M|STRLIST, 0, 0 }, { "Mv", cMove, A|M|STRLIST, 0, 0 }, { "noop", cnoop, A|M|RAWLIST, 0, 0 }, { "collapse", ccollapse, A|MSGLIST, 0, 0 }, { "uncollapse", cuncollapse, A|MSGLIST, 0, 0 }, { "verify", cverify, A|MSGLIST, 0, 0 }, { "decrypt", cdecrypt, A|M|STRLIST, 0, 0 }, { "Decrypt", cDecrypt, A|M|STRLIST, 0, 0 }, { "certsave", ccertsave, A|STRLIST, 0, 0 }, { "rename", crename, M|RAWLIST, 0, 2 }, { "remove", cremove, M|RAWLIST, 0, 1000 }, { "classify", cclassify, A|M|MSGLIST, 0, 0 }, { "junk", cjunk, A|M|MSGLIST, 0, 0 }, { "bad", cjunk, A|M|MSGLIST, 0, 0 }, { "good", cgood, A|M|MSGLIST, 0, 0 }, { "unjunk", cunjunk, A|M|MSGLIST, 0, 0 }, { "ungood", cungood, A|M|MSGLIST, 0, 0 }, { "probability",cprobability, M|RAWLIST, 0, 1000 }, { "show", show, A|MSGLIST, 0, MMNDEL }, { "Show", show, A|MSGLIST, 0, MMNDEL }, { "seen", seen, A|M|MSGLIST, 0, MMNDEL }, { "Seen", seen, A|M|MSGLIST, 0, MMNDEL }, { "fwdignore", fwdigfield, M|RAWLIST, 0, 1000 }, { "fwddiscard",fwdigfield, M|RAWLIST, 0, 1000 }, { "fwdretain", fwdretfield, M|RAWLIST, 0, 1000 }, { "unfwdignore", unfwdignore, M|RAWLIST, 0, 1000 }, { "unfwdretain", unfwdretain, M|RAWLIST, 0, 1000 }, /* { "Header", Header, STRLIST, 0, 1000 }, */ #ifdef DEBUG_COMMANDS { "core", core, M|NOLIST, 0, 0 }, { "clobber", clobber, M|RAWLIST, 0, 1 }, #endif /* DEBUG_COMMANDS */ { 0, 0, 0, 0, 0 } }; heirloom-mailx-12.5/collect.c000066400000000000000000000716311155563371200162000ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)collect.c 2.54 (gritter) 6/16/07"; #endif #endif /* not lint */ /* * Mail -- a mail program * * Collect input from standard input, handling * ~ escapes. */ #include "rcv.h" #include "extern.h" #include #include /* * Read a message from standard output and return a read file to it * or NULL on error. */ /* * The following hokiness with global variables is so that on * receipt of an interrupt signal, the partial message can be salted * away on dead.letter. */ static sighandler_type saveint; /* Previous SIGINT value */ static sighandler_type savehup; /* Previous SIGHUP value */ static sighandler_type savetstp; /* Previous SIGTSTP value */ static sighandler_type savettou; /* Previous SIGTTOU value */ static sighandler_type savettin; /* Previous SIGTTIN value */ static FILE *collf; /* File for saving away */ static int hadintr; /* Have seen one SIGINT so far */ static sigjmp_buf colljmp; /* To get back to work */ static int colljmp_p; /* whether to long jump */ static sigjmp_buf collabort; /* To end collection with error */ static sigjmp_buf pipejmp; /* On broken pipe */ static void onpipe(int signo); static void insertcommand(FILE *fp, char *cmd); static void print_collf(FILE *collf, struct header *hp); static int include_file(FILE *fbuf, char *name, int *linecount, int *charcount, int echo); static struct attachment *read_attachment_data(struct attachment *ap, unsigned number); static struct attachment *append_attachments(struct attachment *attach, char *names); static int exwrite(char *name, FILE *fp, int f); static enum okay makeheader(FILE *fp, struct header *hp); static void mesedit(int c, struct header *hp); static void mespipe(char *cmd); static int forward(char *ms, FILE *fp, int f); static void collstop(int s); static void collint(int s); static void collhup(int s); static int putesc(const char *s, FILE *stream); /*ARGSUSED*/ static void onpipe(int signo) { siglongjmp(pipejmp, 1); } /* * Execute cmd and insert its standard output into fp. */ static void insertcommand(FILE *fp, char *cmd) { FILE *obuf = NULL; char *cp; int c; (void)&obuf; (void)&cp; cp = value("SHELL"); if (sigsetjmp(pipejmp, 1)) goto endpipe; if (cp == NULL) cp = SHELL; if ((obuf = Popen(cmd, "r", cp, 0)) == NULL) { perror(cmd); return; } safe_signal(SIGPIPE, onpipe); while ((c = getc(obuf)) != EOF) putc(c, fp); endpipe: safe_signal(SIGPIPE, SIG_IGN); Pclose(obuf); safe_signal(SIGPIPE, dflpipe); } /* * ~p command. */ static void print_collf(FILE *collf, struct header *hp) { char *lbuf = NULL; FILE *obuf = stdout; struct attachment *ap; char *cp; enum gfield gf; size_t linecnt, maxlines, linesize = 0, linelen, count, count2; (void)&obuf; (void)&cp; fflush(collf); rewind(collf); count = count2 = fsize(collf); if (is_a_tty[0] && is_a_tty[1] && (cp = value("crt")) != NULL) { for (linecnt = 0; fgetline(&lbuf, &linesize, &count2, NULL, collf, 0); linecnt++); rewind(collf); maxlines = (*cp == '\0' ? screensize() : atoi(cp)); maxlines -= 4; if (hp->h_to) maxlines--; if (hp->h_subject) maxlines--; if (hp->h_cc) maxlines--; if (hp->h_bcc) maxlines--; if (hp->h_attach) maxlines--; maxlines -= myaddrs(hp) != NULL || hp->h_from != NULL; maxlines -= value("ORGANIZATION") != NULL || hp->h_organization != NULL; maxlines -= value("replyto") != NULL || hp->h_replyto != NULL; maxlines -= value("sender") != NULL || hp->h_sender != NULL; if (linecnt > maxlines) { cp = get_pager(); if (sigsetjmp(pipejmp, 1)) goto endpipe; obuf = Popen(cp, "w", NULL, 1); if (obuf == NULL) { perror(cp); obuf = stdout; } else safe_signal(SIGPIPE, onpipe); } } fprintf(obuf, catgets(catd, CATSET, 62, "-------\nMessage contains:\n")); gf = GIDENT|GTO|GSUBJECT|GCC|GBCC|GNL|GFILES; if (value("fullnames")) gf |= GCOMMA; puthead(hp, obuf, gf, SEND_TODISP, CONV_NONE, NULL, NULL); while (fgetline(&lbuf, &linesize, &count, &linelen, collf, 1)) prout(lbuf, linelen, obuf); if (hp->h_attach != NULL) { fputs(catgets(catd, CATSET, 63, "Attachments:"), obuf); for (ap = hp->h_attach; ap != NULL; ap = ap->a_flink) { if (ap->a_msgno) fprintf(obuf, " message %u", ap->a_msgno); else fprintf(obuf, " %s", ap->a_name); if (ap->a_flink) putc(',', obuf); } putc('\n', obuf); } endpipe: if (obuf != stdout) { safe_signal(SIGPIPE, SIG_IGN); Pclose(obuf); safe_signal(SIGPIPE, dflpipe); } if (lbuf) free(lbuf); } static int include_file(FILE *fbuf, char *name, int *linecount, int *charcount, int echo) { char *interactive, *linebuf = NULL; size_t linesize = 0, linelen, count; if (fbuf == NULL) fbuf = Fopen(name, "r"); if (fbuf == NULL) { perror(name); return -1; } interactive = value("interactive"); *linecount = 0; *charcount = 0; fflush(fbuf); rewind(fbuf); count = fsize(fbuf); while (fgetline(&linebuf, &linesize, &count, &linelen, fbuf, 0) != NULL) { if (fwrite(linebuf, sizeof *linebuf, linelen, collf) != linelen) { Fclose(fbuf); return -1; } if (interactive != NULL && echo) fwrite(linebuf, sizeof *linebuf, linelen, stdout); (*linecount)++; (*charcount) += linelen; } if (linebuf) free(linebuf); Fclose(fbuf); return 0; } /* * Ask the user to edit file names and other data for the given * attachment. NULL is returned if no file name is given. */ static struct attachment * read_attachment_data(struct attachment *ap, unsigned number) { char prefix[80], *cp; if (ap == NULL) ap = csalloc(1, sizeof *ap); if (ap->a_msgno) { printf("#%u\tmessage %u\n", number, ap->a_msgno); return ap; } snprintf(prefix, sizeof prefix, catgets(catd, CATSET, 50, "#%u\tfilename: "), number); for (;;) { if ((ap->a_name = readtty(prefix, ap->a_name)) == NULL) break; if (access(ap->a_name, R_OK) == 0) break; perror(ap->a_name); } if (ap->a_name && (value("attachment-ask-charset") || (cp = value("sendcharsets")) != NULL && strchr(cp, ',') != NULL)) { snprintf(prefix, sizeof prefix, "#%u\tcharset: ", number); ap->a_charset = readtty(prefix, ap->a_charset); } /* * The "attachment-ask-content-*" variables are left undocumented * since they are for RFC connoisseurs only. */ if (ap->a_name && value("attachment-ask-content-type")) { if (ap->a_content_type == NULL) ap->a_content_type = mime_filecontent(ap->a_name); snprintf(prefix, sizeof prefix, "#%u\tContent-Type: ", number); ap->a_content_type = readtty(prefix, ap->a_content_type); } if (ap->a_name && value("attachment-ask-content-disposition")) { snprintf(prefix, sizeof prefix, "#%u\tContent-Disposition: ", number); ap->a_content_disposition = readtty(prefix, ap->a_content_disposition); } if (ap->a_name && value("attachment-ask-content-id")) { snprintf(prefix, sizeof prefix, "#%u\tContent-ID: ", number); ap->a_content_id = readtty(prefix, ap->a_content_id); } if (ap->a_name && value("attachment-ask-content-description")) { snprintf(prefix, sizeof prefix, "#%u\tContent-Description: ", number); ap->a_content_description = readtty(prefix, ap->a_content_description); } return ap->a_name ? ap : NULL; } /* * Interactively edit the attachment list. */ struct attachment * edit_attachments(struct attachment *attach) { struct attachment *ap, *nap; unsigned attno = 1; for (ap = attach; ap; ap = ap->a_flink) { if ((nap = read_attachment_data(ap, attno)) == NULL) { if (ap->a_blink) ap->a_blink->a_flink = ap->a_flink; else attach = ap->a_flink; if (ap->a_flink) ap->a_flink->a_blink = ap->a_blink; else return attach; } else attno++; } while ((nap = read_attachment_data(NULL, attno)) != NULL) { for (ap = attach; ap && ap->a_flink; ap = ap->a_flink); if (ap) ap->a_flink = nap; nap->a_blink = ap; nap->a_flink = NULL; if (attach == NULL) attach = nap; attno++; } return attach; } /* * Put the given file to the end of the attachment list. */ struct attachment * add_attachment(struct attachment *attach, const char *file) { struct attachment *ap, *nap; if (access(file, R_OK) != 0) return NULL; /*LINTED*/ nap = csalloc(1, sizeof *nap); nap->a_name = salloc(strlen(file) + 1); strcpy(nap->a_name, file); if (attach != NULL) { for (ap = attach; ap->a_flink != NULL; ap = ap->a_flink); ap->a_flink = nap; nap->a_blink = ap; } else { nap->a_blink = NULL; attach = nap; } return attach; } /* * Append the whitespace-separated file names to the end of * the attachment list. */ static struct attachment * append_attachments(struct attachment *attach, char *names) { char *cp; int c; struct attachment *ap; cp = names; while (*cp != '\0' && blankchar(*cp & 0377)) cp++; while (*cp != '\0') { names = cp; while (*cp != '\0' && !blankchar(*cp & 0377)) cp++; c = *cp; *cp++ = '\0'; if (*names != '\0') { if ((ap = add_attachment(attach, names)) == NULL) perror(names); else attach = ap; } if (c == '\0') break; while (*cp != '\0' && blankchar(*cp & 0377)) cp++; } return attach; } FILE * collect(struct header *hp, int printheaders, struct message *mp, char *quotefile, int doprefix, int tflag) { FILE *fbuf; struct ignoretab *quoteig; int lc, cc, escape, eofcount; int c, t; char *linebuf = NULL, *cp, *quote = NULL; size_t linesize; char *tempMail = NULL; int getfields; sigset_t oset, nset; long count; enum sendaction action; sighandler_type savedtop; const char tildehelp[] = "-------------------- ~ ESCAPES ----------------------------\n\ ~~ Quote a single tilde\n\ ~@ [file ...] Edit attachment list\n\ ~b users Add users to \"blind\" cc list\n\ ~c users Add users to cc list\n\ ~d Read in dead.letter\n\ ~e Edit the message buffer\n\ ~f messages Read in messages without indenting lines\n\ ~F messages Same as ~f, but keep all header lines\n\ ~h Prompt for to list, subject, cc, and \"blind\" cc list\n\ ~r file Read a file into the message buffer\n\ ~p Print the message buffer\n\ ~q Abort message composition and save text to dead.letter\n\ ~m messages Read in messages with each line indented\n\ ~M messages Same as ~m, but keep all header lines\n\ ~s subject Set subject\n\ ~t users Add users to to list\n\ ~v Invoke display editor on message\n\ ~w file Write message onto file\n\ ~x Abort message composition and discard text written so far\n\ ~!command Invoke the shell\n\ ~:command Execute a regular command\n\ -----------------------------------------------------------\n"; (void) &escape; (void) &eofcount; (void) &getfields; (void) &tempMail; (void) &tflag; (void) "e; collf = NULL; /* * Start catching signals from here, but we're still die on interrupts * until we're in the main loop. */ sigemptyset(&nset); sigaddset(&nset, SIGINT); sigaddset(&nset, SIGHUP); sigprocmask(SIG_BLOCK, &nset, &oset); handlerpush(collint); if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN) safe_signal(SIGINT, collint); if ((savehup = safe_signal(SIGHUP, SIG_IGN)) != SIG_IGN) safe_signal(SIGHUP, collhup); savetstp = safe_signal(SIGTSTP, collstop); savettou = safe_signal(SIGTTOU, collstop); savettin = safe_signal(SIGTTIN, collstop); if (sigsetjmp(collabort, 1)) { if (tempMail != NULL) { rm(tempMail); Ftfree(&tempMail); } goto err; } if (sigsetjmp(colljmp, 1)) { if (tempMail != NULL) { rm(tempMail); Ftfree(&tempMail); } goto err; } sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL); noreset++; if ((collf = Ftemp(&tempMail, "Rs", "w+", 0600, 1)) == NULL) { perror(catgets(catd, CATSET, 51, "temporary mail file")); goto err; } unlink(tempMail); Ftfree(&tempMail); if ((cp = value("MAILX_HEAD")) != NULL) { if (is_a_tty[0]) putesc(cp, stdout); putesc(cp, collf); } /* * If we are going to prompt for a subject, * refrain from printing a newline after * the headers (since some people mind). */ getfields = 0; if (!tflag) { t = GTO|GSUBJECT|GCC|GNL; if (value("fullnames")) t |= GCOMMA; if (hp->h_subject == NULL && value("interactive") != NULL && (value("ask") != NULL || value("asksub") != NULL)) t &= ~GNL, getfields |= GSUBJECT; if (hp->h_to == NULL && value("interactive") != NULL) t &= ~GNL, getfields |= GTO; if (value("bsdcompat") == NULL && value("askatend") == NULL && value("interactive")) { if (hp->h_bcc == NULL && value("askbcc")) t &= ~GNL, getfields |= GBCC; if (hp->h_cc == NULL && value("askcc")) t &= ~GNL, getfields |= GCC; } if (printheaders) { puthead(hp, stdout, t, SEND_TODISP, CONV_NONE, NULL, NULL); fflush(stdout); } } /* * Quote an original message */ if (mp != NULL && (doprefix || (quote = value("quote")) != NULL)) { quoteig = allignore; action = SEND_QUOTE; if (doprefix) { quoteig = fwdignore; if ((cp = value("fwdheading")) == NULL) cp = "-------- Original Message --------"; if (*cp) { fprintf(collf, "%s\n", cp); fprintf(stdout, "%s\n", cp); } } else if (strcmp(quote, "noheading") == 0) { /*EMPTY*/; } else if (strcmp(quote, "headers") == 0) { quoteig = ignore; } else if (strcmp(quote, "allheaders") == 0) { quoteig = NULL; action = SEND_QUOTE_ALL; } else { cp = hfield("from", mp); if (cp != NULL) { mime_write(cp, strlen(cp), collf, CONV_FROMHDR, TD_NONE, NULL, (size_t) 0, NULL, NULL); mime_write(cp, strlen(cp), stdout, CONV_FROMHDR, TD_NONE, NULL, (size_t) 0, NULL, NULL); fwrite(catgets(catd, CATSET, 52, " wrote:\n\n"), sizeof(char), 9, collf); fwrite(catgets(catd, CATSET, 52, " wrote:\n\n"), sizeof(char), 9, stdout); } } cp = value("indentprefix"); if (cp != NULL && *cp == '\0') cp = "\t"; send(mp, collf, quoteig, doprefix ? NULL : cp, action, NULL); send(mp, stdout, quoteig, doprefix ? NULL : cp, action, NULL); } if ((cp = value("escape")) != NULL) escape = *cp; else escape = ESCAPE; eofcount = 0; hadintr = 0; if (!sigsetjmp(colljmp, 1)) { if (getfields) grabh(hp, getfields, 1); if (quotefile != NULL) { if (include_file(NULL, quotefile, &lc, &cc, 1) != 0) goto err; } } else { /* * Come here for printing the after-signal message. * Duplicate messages won't be printed because * the write is aborted if we get a SIGTTOU. */ cont: if (hadintr) { fflush(stdout); fprintf(stderr, catgets(catd, CATSET, 53, "\n(Interrupt -- one more to kill letter)\n")); } else { printf(catgets(catd, CATSET, 54, "(continue)\n")); fflush(stdout); } } if (value("interactive") == NULL && tildeflag <= 0 && !is_a_tty[0] && !tflag) { /* * No tilde escapes, interrupts not expected. Copy * standard input the simple way. */ linebuf = srealloc(linebuf, linesize = BUFSIZ); while ((count = fread(linebuf, sizeof *linebuf, linesize, stdin)) > 0) { if (fwrite(linebuf, sizeof *linebuf, count, collf) != count) goto err; } goto out; } for (;;) { colljmp_p = 1; count = readline(stdin, &linebuf, &linesize); colljmp_p = 0; if (count < 0) { if (value("interactive") != NULL && value("ignoreeof") != NULL && ++eofcount < 25) { printf(catgets(catd, CATSET, 55, "Use \".\" to terminate letter\n")); continue; } break; } if (tflag && count == 0) { rewind(collf); if (makeheader(collf, hp) != OKAY) goto err; rewind(collf); tflag = 0; continue; } eofcount = 0; hadintr = 0; if (linebuf[0] == '.' && linebuf[1] == '\0' && value("interactive") != NULL && (value("dot") != NULL || value("ignoreeof") != NULL)) break; if (linebuf[0] != escape || (value("interactive") == NULL && tildeflag == 0 || tildeflag < 0)) { if (putline(collf, linebuf, count) < 0) goto err; continue; } c = linebuf[1]; switch (c) { default: /* * On double escape, just send the single one. * Otherwise, it's an error. */ if (c == escape) { if (putline(collf, &linebuf[1], count - 1) < 0) goto err; else break; } printf(catgets(catd, CATSET, 56, "Unknown tilde escape.\n")); break; #ifdef DEBUG_COMMANDS case 'C': /* * Dump core. */ core(NULL); break; #endif /* DEBUG_COMMANDS */ case '!': /* * Shell escape, send the balance of the * line to sh -c. */ shell(&linebuf[2]); break; case ':': case '_': /* * Escape to command mode, but be nice! */ inhook = 0; execute(&linebuf[2], 1, count - 2); goto cont; case '.': /* * Simulate end of file on input. */ goto out; case 'x': /* * Same as 'q', but no dead.letter saving. */ hadintr++; collint(0); exit(1); /*NOTREACHED*/ case 'q': /* * Force a quit of sending mail. * Act like an interrupt happened. */ hadintr++; collint(SIGINT); exit(1); /*NOTREACHED*/ case 'h': /* * Grab a bunch of headers. */ do grabh(hp, GTO|GSUBJECT|GCC|GBCC, value("bsdcompat") != NULL && value("bsdorder") != NULL); while (hp->h_to == NULL); goto cont; case 'H': /* * Grab extra headers. */ do grabh(hp, GEXTRA, 0); while (check_from_and_sender(hp->h_from, hp->h_sender)); goto cont; case 't': /* * Add to the To list. */ while ((hp->h_to = checkaddrs(cat(hp->h_to, sextract(&linebuf[2], GTO|GFULL)))) == NULL); break; case 's': /* * Set the Subject list. */ cp = &linebuf[2]; while (whitechar(*cp & 0377)) cp++; hp->h_subject = savestr(cp); break; case '@': /* * Edit the attachment list. */ if (linebuf[2] != '\0') hp->h_attach = append_attachments(hp->h_attach, &linebuf[2]); else hp->h_attach = edit_attachments(hp->h_attach); break; case 'c': /* * Add to the CC list. */ hp->h_cc = checkaddrs(cat(hp->h_cc, sextract(&linebuf[2], GCC|GFULL))); break; case 'b': /* * Add stuff to blind carbon copies list. */ hp->h_bcc = checkaddrs(cat(hp->h_bcc, sextract(&linebuf[2], GBCC|GFULL))); break; case 'd': strncpy(linebuf + 2, getdeadletter(), linesize - 2); linebuf[linesize-1]='\0'; /*FALLTHRU*/ case 'r': case '<': /* * Invoke a file: * Search for the file name, * then open it and copy the contents to collf. */ cp = &linebuf[2]; while (whitechar(*cp & 0377)) cp++; if (*cp == '\0') { printf(catgets(catd, CATSET, 57, "Interpolate what file?\n")); break; } if (*cp == '!') { insertcommand(collf, cp + 1); break; } cp = expand(cp); if (cp == NULL) break; if (is_dir(cp)) { printf(catgets(catd, CATSET, 58, "%s: Directory\n"), cp); break; } if ((fbuf = Fopen(cp, "r")) == NULL) { perror(cp); break; } printf(catgets(catd, CATSET, 59, "\"%s\" "), cp); fflush(stdout); if (include_file(fbuf, cp, &lc, &cc, 0) != 0) goto err; printf(catgets(catd, CATSET, 60, "%d/%d\n"), lc, cc); break; case 'i': /* * Insert an environment variable into the file. */ cp = &linebuf[2]; while (whitechar(*cp & 0377)) cp++; if ((cp = value(cp)) == NULL || *cp == '\0') break; if (is_a_tty[0]) putesc(cp, stdout); putesc(cp, collf); break; case 'a': case 'A': /* * Insert the contents of a signature variable. */ if ((cp = value(c == 'a' ? "sign" : "Sign")) != NULL && *cp != '\0') { if (is_a_tty[0]) putesc(cp, stdout); putesc(cp, collf); } break; case 'w': /* * Write the message on a file. */ cp = &linebuf[2]; while (blankchar(*cp & 0377)) cp++; if (*cp == '\0') { fprintf(stderr, catgets(catd, CATSET, 61, "Write what file!?\n")); break; } if ((cp = expand(cp)) == NULL) break; rewind(collf); exwrite(cp, collf, 1); break; case 'm': case 'M': case 'f': case 'F': /* * Interpolate the named messages, if we * are in receiving mail mode. Does the * standard list processing garbage. * If ~f is given, we don't shift over. */ if (forward(linebuf + 2, collf, c) < 0) goto err; goto cont; case '?': fputs(tildehelp, stdout); break; case 'p': /* * Print out the current state of the * message without altering anything. */ print_collf(collf, hp); goto cont; case '|': /* * Pipe message through command. * Collect output as new message. */ rewind(collf); mespipe(&linebuf[2]); goto cont; case 'v': case 'e': /* * Edit the current message. * 'e' means to use EDITOR * 'v' means to use VISUAL */ rewind(collf); mesedit(c, value("editheaders") ? hp : NULL); goto cont; } } goto out; err: if (collf != NULL) { Fclose(collf); collf = NULL; } out: if (collf != NULL) { if ((cp = value("MAILX_TAIL")) != NULL) { if (is_a_tty[0]) putesc(cp, stdout); fflush(collf); putesc(cp, collf); } rewind(collf); } handlerpop(); noreset--; sigemptyset(&nset); sigaddset(&nset, SIGINT); sigaddset(&nset, SIGHUP); #ifndef OLDBUG sigprocmask(SIG_BLOCK, &nset, (sigset_t *)NULL); #else sigprocmask(SIG_BLOCK, &nset, &oset); #endif safe_signal(SIGINT, saveint); safe_signal(SIGHUP, savehup); safe_signal(SIGTSTP, savetstp); safe_signal(SIGTTOU, savettou); safe_signal(SIGTTIN, savettin); sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL); return collf; } /* * Write a file, ex-like if f set. */ static int exwrite(char *name, FILE *fp, int f) { FILE *of; int c; long cc; int lc; if (f) { printf("\"%s\" ", name); fflush(stdout); } if ((of = Fopen(name, "a")) == NULL) { perror(NULL); return(-1); } lc = 0; cc = 0; while ((c = getc(fp)) != EOF) { cc++; if (c == '\n') lc++; putc(c, of); if (ferror(of)) { perror(name); Fclose(of); return(-1); } } Fclose(of); printf(catgets(catd, CATSET, 65, "%d/%ld\n"), lc, cc); fflush(stdout); return(0); } static enum okay makeheader(FILE *fp, struct header *hp) { char *tempEdit; FILE *nf; int c; if ((nf = Ftemp(&tempEdit, "Re", "w+", 0600, 1)) == NULL) { perror(catgets(catd, CATSET, 66, "temporary mail edit file")); Fclose(nf); unlink(tempEdit); Ftfree(&tempEdit); return STOP; } unlink(tempEdit); Ftfree(&tempEdit); extract_header(fp, hp); while ((c = getc(fp)) != EOF) putc(c, nf); if (fp != collf) Fclose(collf); Fclose(fp); collf = nf; if (check_from_and_sender(hp->h_from, hp->h_sender)) return STOP; return OKAY; } /* * Edit the message being collected on fp. * On return, make the edit file the new temp file. */ static void mesedit(int c, struct header *hp) { sighandler_type sigint = safe_signal(SIGINT, SIG_IGN); FILE *nf = run_editor(collf, (off_t)-1, c, 0, hp, NULL, SEND_MBOX, sigint); if (nf != NULL) { if (hp) { rewind(nf); makeheader(nf, hp); } else { fseek(nf, 0L, SEEK_END); Fclose(collf); collf = nf; } } safe_signal(SIGINT, sigint); } /* * Pipe the message through the command. * Old message is on stdin of command; * New message collected from stdout. * Sh -c must return 0 to accept the new message. */ static void mespipe(char *cmd) { FILE *nf; sighandler_type sigint = safe_signal(SIGINT, SIG_IGN); char *tempEdit; char *shell; if ((nf = Ftemp(&tempEdit, "Re", "w+", 0600, 1)) == NULL) { perror(catgets(catd, CATSET, 66, "temporary mail edit file")); goto out; } fflush(collf); unlink(tempEdit); Ftfree(&tempEdit); /* * stdin = current message. * stdout = new message. */ if ((shell = value("SHELL")) == NULL) shell = SHELL; if (run_command(shell, 0, fileno(collf), fileno(nf), "-c", cmd, NULL) < 0) { Fclose(nf); goto out; } if (fsize(nf) == 0) { fprintf(stderr, catgets(catd, CATSET, 67, "No bytes from \"%s\" !?\n"), cmd); Fclose(nf); goto out; } /* * Take new files. */ fseek(nf, 0L, SEEK_END); Fclose(collf); collf = nf; out: safe_signal(SIGINT, sigint); } /* * Interpolate the named messages into the current * message, preceding each line with a tab. * Return a count of the number of characters now in * the message, or -1 if an error is encountered writing * the message temporary. The flag argument is 'm' if we * should shift over and 'f' if not. */ static int forward(char *ms, FILE *fp, int f) { int *msgvec; struct ignoretab *ig; char *tabst; enum sendaction action; /*LINTED*/ msgvec = (int *)salloc((msgCount+1) * sizeof *msgvec); if (msgvec == NULL) return(0); if (getmsglist(ms, msgvec, 0) < 0) return(0); if (*msgvec == 0) { *msgvec = first(0, MMNORM); if (*msgvec == 0) { printf(catgets(catd, CATSET, 68, "No appropriate messages\n")); return(0); } msgvec[1] = 0; } if (f == 'f' || f == 'F') tabst = NULL; else if ((tabst = value("indentprefix")) == NULL) tabst = "\t"; ig = upperchar(f) ? (struct ignoretab *)NULL : ignore; action = upperchar(f) ? SEND_QUOTE_ALL : SEND_QUOTE; printf(catgets(catd, CATSET, 69, "Interpolating:")); for (; *msgvec != 0; msgvec++) { struct message *mp = message + *msgvec - 1; touch(mp); printf(" %d", *msgvec); if (send(mp, fp, ig, tabst, action, NULL) < 0) { perror(catgets(catd, CATSET, 70, "temporary mail file")); return(-1); } } printf("\n"); return(0); } /* * Print (continue) when continued after ^Z. */ /*ARGSUSED*/ static void collstop(int s) { sighandler_type old_action = safe_signal(s, SIG_DFL); sigset_t nset; sigemptyset(&nset); sigaddset(&nset, s); sigprocmask(SIG_UNBLOCK, &nset, (sigset_t *)NULL); kill(0, s); sigprocmask(SIG_BLOCK, &nset, (sigset_t *)NULL); safe_signal(s, old_action); if (colljmp_p) { colljmp_p = 0; hadintr = 0; siglongjmp(colljmp, 1); } } /* * On interrupt, come here to save the partial message in ~/dead.letter. * Then jump out of the collection loop. */ /*ARGSUSED*/ static void collint(int s) { /* * the control flow is subtle, because we can be called from ~q. */ if (!hadintr) { if (value("ignore") != NULL) { puts("@"); fflush(stdout); clearerr(stdin); return; } hadintr = 1; siglongjmp(colljmp, 1); } exit_status |= 04; rewind(collf); if (value("save") != NULL && s != 0) savedeadletter(collf); siglongjmp(collabort, 1); } /*ARGSUSED*/ static void collhup(int s) { rewind(collf); savedeadletter(collf); /* * Let's pretend nobody else wants to clean up, * a true statement at this time. */ exit(1); } void savedeadletter(FILE *fp) { FILE *dbuf; int c, lines = 0, bytes = 0; char *cp; if (fsize(fp) == 0) return; cp = getdeadletter(); c = umask(077); dbuf = Fopen(cp, "a"); umask(c); if (dbuf == NULL) return; printf("\"%s\" ", cp); while ((c = getc(fp)) != EOF) { putc(c, dbuf); bytes++; if (c == '\n') lines++; } Fclose(dbuf); printf("%d/%d\n", lines, bytes); rewind(fp); } static int putesc(const char *s, FILE *stream) { int n = 0; while (s[0]) { if (s[0] == '\\') { if (s[1] == 't') { putc('\t', stream); n++; s += 2; continue; } if (s[1] == 'n') { putc('\n', stream); n++; s += 2; continue; } } putc(s[0]&0377, stream); n++; s++; } putc('\n', stream); return ++n; } heirloom-mailx-12.5/def.h000066400000000000000000000516631155563371200153210ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Sccsid @(#)def.h 2.104 (gritter) 3/4/06 */ /* * Mail -- a mail program * * Author: Kurt Shoens (UCB) March 25, 1978 */ #if !defined (NI_MAXHOST) || (NI_MAXHOST) < 1025 #undef NI_MAXHOST #define NI_MAXHOST 1025 #endif #define APPEND /* New mail goes to end of mailbox */ #define ESCAPE '~' /* Default escape for sending */ #ifndef MAXPATHLEN #ifdef PATH_MAX #define MAXPATHLEN PATH_MAX #else #define MAXPATHLEN 1024 #endif #endif #ifndef PATHSIZE #define PATHSIZE MAXPATHLEN /* Size of pathnames throughout */ #endif #define HSHSIZE 59 /* Hash size for aliases and vars */ #define LINESIZE 2560 /* max readable line width */ #define STRINGSIZE ((unsigned) 128)/* Dynamic allocation units */ #define MAXARGC 1024 /* Maximum list of raw strings */ #define MAXEXP 25 /* Maximum expansion of aliases */ #define equal(a, b) (strcmp(a,b)==0)/* A nice function to string compare */ #ifdef HAVE_CATGETS #define CATSET 1 #else /* !HAVE_CATGETS */ #define catgets(a, b, c, d) (d) #endif /* !HAVE_CATGETS */ typedef void (*sighandler_type)(int); enum okay { STOP = 0, OKAY = 1 }; enum mimeenc { MIME_NONE, /* message is not in MIME format */ MIME_BIN, /* message is in binary encoding */ MIME_8B, /* message is in 8bit encoding */ MIME_7B, /* message is in 7bit encoding */ MIME_QP, /* message is quoted-printable */ MIME_B64 /* message is in base64 encoding */ }; enum conversion { CONV_NONE, /* no conversion */ CONV_7BIT, /* no conversion, is 7bit */ CONV_FROMQP, /* convert from quoted-printable */ CONV_TOQP, /* convert to quoted-printable */ CONV_8BIT, /* convert to 8bit (iconv) */ CONV_FROMB64, /* convert from base64 */ CONV_FROMB64_T, /* convert from base64/text */ CONV_TOB64, /* convert to base64 */ CONV_FROMHDR, /* convert from RFC1522 format */ CONV_TOHDR, /* convert to RFC1522 format */ CONV_TOHDR_A /* convert addresses for header */ }; enum sendaction { SEND_MBOX, /* no conversion to perform */ SEND_RFC822, /* no conversion, no From_ line */ SEND_TODISP, /* convert to displayable form */ SEND_TODISP_ALL, /* same, include all MIME parts */ SEND_SHOW, /* convert to 'show' command form */ SEND_TOSRCH, /* convert for IMAP SEARCH */ SEND_TOFLTR, /* convert for junk mail filtering */ SEND_TOFILE, /* convert for saving body to a file */ SEND_TOPIPE, /* convert for pipe-content/subc. */ SEND_QUOTE, /* convert for quoting */ SEND_QUOTE_ALL, /* same, include all MIME parts */ SEND_DECRYPT /* decrypt */ }; enum mimecontent { MIME_UNKNOWN, /* unknown content */ MIME_SUBHDR, /* inside a multipart subheader */ MIME_822, /* message/rfc822 content */ MIME_MESSAGE, /* other message/ content */ MIME_TEXT_PLAIN, /* text/plain content */ MIME_TEXT_HTML, /* text/html content */ MIME_TEXT, /* other text/ content */ MIME_ALTERNATIVE, /* multipart/alternative content */ MIME_DIGEST, /* multipart/digest content */ MIME_MULTI, /* other multipart/ content */ MIME_PKCS7, /* PKCS7 content */ MIME_DISCARD /* content is discarded */ }; enum mimeclean { MIME_CLEAN = 000, /* plain RFC 2822 message */ MIME_HIGHBIT = 001, /* characters >= 0200 */ MIME_LONGLINES = 002, /* has lines too long for RFC 2822 */ MIME_CTRLCHAR = 004, /* contains control characters */ MIME_HASNUL = 010, /* contains \0 characters */ MIME_NOTERMNL = 020 /* lacks a terminating newline */ }; enum tdflags { TD_NONE = 0, /* no display conversion */ TD_ISPR = 01, /* use isprint() checks */ TD_ICONV = 02, /* use iconv() */ TD_DELCTRL = 04 /* delete control characters */ }; struct str { char *s; /* the string's content */ size_t l; /* the stings's length */ }; enum protocol { PROTO_FILE, /* refers to a local file */ PROTO_POP3, /* is a pop3 server string */ PROTO_IMAP, /* is an imap server string */ PROTO_MAILDIR, /* refers to a maildir folder */ PROTO_UNKNOWN /* unknown protocol */ }; struct sock { /* data associated with a socket */ int s_fd; /* file descriptor */ #ifdef USE_SSL int s_use_ssl; /* SSL is used */ #if defined (USE_NSS) void *s_prfd; /* NSPR file descriptor */ #elif defined (USE_OPENSSL) void *s_ssl; /* SSL object */ void *s_ctx; /* SSL context object */ #endif /* SSL library specific */ #endif /* USE_SSL */ char *s_wbuf; /* for buffered writes */ int s_wbufsize; /* allocated size of s_buf */ int s_wbufpos; /* position of first empty data byte */ char s_rbuf[LINESIZE+1]; /* for buffered reads */ char *s_rbufptr; /* read pointer to s_rbuf */ int s_rsz; /* size of last read in s_rbuf */ char *s_desc; /* description of error messages */ void (*s_onclose)(void); /* execute on close */ }; struct mailbox { struct sock mb_sock; /* socket structure */ enum { MB_NONE = 000, /* no reply expected */ MB_COMD = 001, /* command reply expected */ MB_MULT = 002, /* multiline reply expected */ MB_PREAUTH = 004, /* not in authenticated state */ MB_BYE = 010 /* may accept a BYE state */ } mb_active; FILE *mb_itf; /* temp file with messages, read open */ FILE *mb_otf; /* same, write open */ char *mb_sorted; /* sort method */ enum { MB_VOID, /* no type (e. g. connection failed) */ MB_FILE, /* local file */ MB_POP3, /* POP3 mailbox */ MB_IMAP, /* IMAP mailbox */ MB_MAILDIR, /* maildir folder */ MB_CACHE /* cached mailbox */ } mb_type; /* type of mailbox */ enum { MB_DELE = 01, /* may delete messages in mailbox */ MB_EDIT = 02 /* may edit messages in mailbox */ } mb_perm; int mb_compressed; /* is a compressed mbox file */ int mb_threaded; /* mailbox has been threaded */ enum mbflags { MB_NOFLAGS = 000, MB_UIDPLUS = 001 /* supports IMAP UIDPLUS */ } mb_flags; unsigned long mb_uidvalidity; /* IMAP unique identifier validity */ char *mb_imap_account; /* name of current IMAP account */ char *mb_imap_mailbox; /* name of current IMAP mailbox */ char *mb_cache_directory; /* name of cache directory */ }; enum needspec { NEED_UNSPEC, /* unspecified need, don't fetch */ NEED_HEADER, /* need the header of a message */ NEED_BODY /* need header and body of a message */ }; enum havespec { HAVE_NOTHING = 0, /* nothing downloaded yet */ HAVE_HEADER = 01, /* header is downloaded */ HAVE_BODY = 02 /* entire message is downloaded */ }; /* * flag bits. Attention: Flags that are used in cache.c may not change. */ enum mflag { MUSED = (1<<0), /* entry is used, but this bit isn't */ MDELETED = (1<<1), /* entry has been deleted */ MSAVED = (1<<2), /* entry has been saved */ MTOUCH = (1<<3), /* entry has been noticed */ MPRESERVE = (1<<4), /* keep entry in sys mailbox */ MMARK = (1<<5), /* message is marked! */ MODIFY = (1<<6), /* message has been modified */ MNEW = (1<<7), /* message has never been seen */ MREAD = (1<<8), /* message has been read sometime. */ MSTATUS = (1<<9), /* message status has changed */ MBOX = (1<<10), /* Send this to mbox, regardless */ MNOFROM = (1<<11), /* no From line */ MHIDDEN = (1<<12), /* message is hidden to user */ MFULLYCACHED = (1<<13), /* message is completely cached */ MBOXED = (1<<14), /* message has been sent to mbox */ MUNLINKED = (1<<15), /* message was unlinked from cache */ MNEWEST = (1<<16), /* message is very new (newmail) */ MFLAG = (1<<17), /* message has been flagged recently */ MUNFLAG = (1<<18), /* message has been unflagged */ MFLAGGED = (1<<19), /* message is `flagged' */ MANSWER = (1<<20), /* message has been answered recently */ MUNANSWER = (1<<21), /* message has been unanswered */ MANSWERED = (1<<22), /* message is `answered' */ MDRAFT = (1<<23), /* message has been drafted recently */ MUNDRAFT = (1<<24), /* message has been undrafted */ MDRAFTED = (1<<25), /* message is marked as `draft' */ MKILL = (1<<26), /* message has been killed */ MOLDMARK = (1<<27), /* messages was marked previously */ MJUNK = (1<<28) /* message is classified as junk */ }; struct mimepart { enum mflag m_flag; /* flags */ enum havespec m_have; /* downloaded parts of the part */ int m_block; /* block number of this part */ size_t m_offset; /* offset in block of part */ size_t m_size; /* Bytes in the part */ size_t m_xsize; /* Bytes in the full part */ long m_lines; /* Lines in the message */ long m_xlines; /* Lines in the full message */ time_t m_time; /* time the message was sent */ char *m_from; /* message sender */ struct mimepart *m_nextpart; /* next part at same level */ struct mimepart *m_multipart; /* parts of multipart */ struct mimepart *m_parent; /* enclosing multipart part */ char *m_ct_type; /* content-type */ char *m_ct_type_plain; /* content-type without specs */ enum mimecontent m_mimecontent; /* same in enum */ char *m_charset; /* charset */ char *m_ct_transfer_enc; /* content-transfer-encoding */ enum mimeenc m_mimeenc; /* same in enum */ char *m_partstring; /* part level string */ char *m_filename; /* attachment filename */ }; struct message { enum mflag m_flag; /* flags */ enum havespec m_have; /* downloaded parts of the message */ int m_block; /* block number of this message */ size_t m_offset; /* offset in block of message */ size_t m_size; /* Bytes in the message */ size_t m_xsize; /* Bytes in the full message */ long m_lines; /* Lines in the message */ long m_xlines; /* Lines in the full message */ time_t m_time; /* time the message was sent */ time_t m_date; /* time in the 'Date' field */ unsigned m_idhash; /* hash on Message-ID for threads */ unsigned long m_uid; /* IMAP unique identifier */ struct message *m_child; /* first child of this message */ struct message *m_younger; /* younger brother of this message */ struct message *m_elder; /* elder brother of this message */ struct message *m_parent; /* parent of this message */ unsigned m_level; /* thread level of message */ long m_threadpos; /* position in threaded display */ float m_score; /* score of message */ char *m_maildir_file; /* original maildir file of msg */ unsigned m_maildir_hash; /* hash of file name in maildir sub */ int m_collapsed; /* collapsed thread information */ }; /* * Given a file address, determine the block number it represents. */ #define mailx_blockof(off) ((int) ((off) / 4096)) #define mailx_offsetof(off) ((int) ((off) % 4096)) #define mailx_positionof(block, offset) ((off_t)(block) * 4096 + (offset)) /* * Argument types. */ enum argtype { MSGLIST = 0, /* Message list type */ STRLIST = 1, /* A pure string */ RAWLIST = 2, /* Shell string list */ NOLIST = 3, /* Just plain 0 */ NDMLIST = 4, /* Message list, no defaults */ ECHOLIST= 5, /* Like raw list, but keep quote chars */ P = 040, /* Autoprint dot after command */ I = 0100, /* Interactive command bit */ M = 0200, /* Legal from send mode bit */ W = 0400, /* Illegal when read only bit */ F = 01000, /* Is a conditional command */ T = 02000, /* Is a transparent command */ R = 04000, /* Cannot be called from collect */ A = 010000 /* Needs an active mailbox */ }; /* * Oft-used mask values */ #define MMNORM (MDELETED|MSAVED|MHIDDEN)/* Look at both save and delete bits */ #define MMNDEL (MDELETED|MHIDDEN) /* Look only at deleted bit */ /* * Format of the command description table. * The actual table is declared and initialized * in lex.c */ struct cmd { char *c_name; /* Name of command */ int (*c_func)(void *); /* Implementor of the command */ enum argtype c_argtype; /* Type of arglist (see below) */ short c_msgflag; /* Required flags of messages */ short c_msgmask; /* Relevant flags of messages */ }; /* Yechh, can't initialize unions */ #define c_minargs c_msgflag /* Minimum argcount for RAWLIST */ #define c_maxargs c_msgmask /* Max argcount for RAWLIST */ /* * Structure used to return a break down of a head * line (hats off to Bill Joy!) */ struct headline { char *l_from; /* The name of the sender */ char *l_tty; /* His tty string (if any) */ char *l_date; /* The entire date string */ }; enum gfield { GTO = 1, /* Grab To: line */ GSUBJECT= 2, /* Likewise, Subject: line */ GCC = 4, /* And the Cc: line */ GBCC = 8, /* And also the Bcc: line */ GNL = 16, /* Print blank line after */ GDEL = 32, /* Entity removed from list */ GCOMMA = 64, /* detract puts in commas */ GUA = 128, /* User-Agent field */ GMIME = 256, /* MIME 1.0 fields */ GMSGID = 512, /* a Message-ID */ /* 1024 */ /* unused */ GIDENT = 2048, /* From:, Reply-To: and Organization: field */ GREF = 4096, /* References: field */ GDATE = 8192, /* Date: field */ GFULL = 16384, /* include full names */ GSKIN = 32768, /* skin names */ GEXTRA = 65536, /* extra fields */ GFILES = 131072 /* include filename addresses */ }; #define GMASK (GTO|GSUBJECT|GCC|GBCC) /* Mask of places from whence */ #define visible(mp) (((mp)->m_flag&(MDELETED|MHIDDEN|MKILL))==0|| \ dot==(mp) && (mp)->m_flag&MKILL) /* * Structure used to pass about the current * state of the user-typed message header. */ struct header { struct name *h_to; /* Dynamic "To:" string */ char *h_subject; /* Subject string */ struct name *h_cc; /* Carbon copies string */ struct name *h_bcc; /* Blind carbon copies */ struct name *h_ref; /* References */ struct name *h_smopts; /* Sendmail options */ struct attachment *h_attach; /* MIME attachments */ char *h_charset; /* preferred charset */ struct name *h_from; /* overridden "From:" field */ struct name *h_replyto; /* overridden "Reply-To:" field */ struct name *h_sender; /* overridden "Sender:" field */ char *h_organization; /* overridden "Organization:" field */ }; /* * Structure of namelist nodes used in processing * the recipients of mail and aliases and all that * kind of stuff. */ struct name { struct name *n_flink; /* Forward link in list. */ struct name *n_blink; /* Backward list link */ enum gfield n_type; /* From which list it came */ char *n_name; /* This fella's name */ char *n_fullname; /* Sometimes, name including comment */ }; /* * Structure of a MIME attachment. */ struct attachment { struct attachment *a_flink; /* Forward link in list. */ struct attachment *a_blink; /* Backward list link */ char *a_name; /* file name */ char *a_content_type; /* content type */ char *a_content_disposition; /* content disposition */ char *a_content_id; /* content id */ char *a_content_description; /* content description */ char *a_charset; /* character set */ int a_msgno; /* message number */ }; /* * Structure of a variable node. All variables are * kept on a singly-linked list of these, rooted by * "variables" */ struct var { struct var *v_link; /* Forward link to next variable */ char *v_name; /* The variable's name */ char *v_value; /* And it's current value */ }; struct group { struct group *ge_link; /* Next person in this group */ char *ge_name; /* This person's user name */ }; struct grouphead { struct grouphead *g_link; /* Next grouphead in list */ char *g_name; /* Name of this group */ struct group *g_list; /* Users in group. */ }; /* * Structure of the hash table of ignored header fields */ struct ignoretab { int i_count; /* Number of entries */ struct ignore { struct ignore *i_link; /* Next ignored field in bucket */ char *i_field; /* This ignored field */ } *i_head[HSHSIZE]; }; /* * Token values returned by the scanner used for argument lists. * Also, sizes of scanner-related things. */ enum ltoken { TEOL = 0, /* End of the command line */ TNUMBER = 1, /* A message number */ TDASH = 2, /* A simple dash */ TSTRING = 3, /* A string (possibly containing -) */ TDOT = 4, /* A "." */ TUP = 5, /* An "^" */ TDOLLAR = 6, /* A "$" */ TSTAR = 7, /* A "*" */ TOPEN = 8, /* An '(' */ TCLOSE = 9, /* A ')' */ TPLUS = 10, /* A '+' */ TERROR = 11, /* A lexical error */ TCOMMA = 12, /* A ',' */ TSEMI = 13, /* A ';' */ TBACK = 14 /* A '`' */ }; #define REGDEP 2 /* Maximum regret depth. */ /* * Constants for conditional commands. These describe whether * we should be executing stuff or not. */ enum condition { CANY = 0, /* Execute in send or receive mode */ CRCV = 1, /* Execute in receive mode only */ CSEND = 2, /* Execute in send mode only */ CTERM = 3, /* Execute only if stdin is a tty */ CNONTERM= 4 /* Execute only if stdin not tty */ }; /* * For the 'shortcut' and 'unshortcut' functionality. */ struct shortcut { struct shortcut *sh_next; /* next shortcut in list */ char *sh_short; /* shortcut string */ char *sh_long; /* expanded form */ }; /* * Kludges to handle the change from setexit / reset to setjmp / longjmp */ #define setexit() sigsetjmp(srbuf, 1) #define reset(x) siglongjmp(srbuf, x) /* * Locale-independent character classes. */ enum { C_CNTRL = 0000, C_BLANK = 0001, C_WHITE = 0002, C_SPACE = 0004, C_PUNCT = 0010, C_OCTAL = 0020, C_DIGIT = 0040, C_UPPER = 0100, C_LOWER = 0200 }; extern const unsigned char class_char[]; #define asciichar(c) ((unsigned)(c) <= 0177) #define alnumchar(c) (asciichar(c)&&(class_char[c]&\ (C_DIGIT|C_OCTAL|C_UPPER|C_LOWER))) #define alphachar(c) (asciichar(c)&&(class_char[c]&(C_UPPER|C_LOWER))) #define blankchar(c) (asciichar(c)&&(class_char[c]&(C_BLANK))) #define cntrlchar(c) (asciichar(c)&&(class_char[c]==C_CNTRL)) #define digitchar(c) (asciichar(c)&&(class_char[c]&(C_DIGIT|C_OCTAL))) #define lowerchar(c) (asciichar(c)&&(class_char[c]&(C_LOWER))) #define punctchar(c) (asciichar(c)&&(class_char[c]&(C_PUNCT))) #define spacechar(c) (asciichar(c)&&(class_char[c]&(C_BLANK|C_SPACE|C_WHITE))) #define upperchar(c) (asciichar(c)&&(class_char[c]&(C_UPPER))) #define whitechar(c) (asciichar(c)&&(class_char[c]&(C_BLANK|C_WHITE))) #define octalchar(c) (asciichar(c)&&(class_char[c]&(C_OCTAL))) #define upperconv(c) (lowerchar(c) ? (c)-'a'+'A' : (c)) #define lowerconv(c) (upperchar(c) ? (c)-'A'+'a' : (c)) /* RFC 822, 3.2. */ #define fieldnamechar(c) (asciichar(c)&&(c)>040&&(c)!=0177&&(c)!=':') /* * Truncate a file to the last character written. This is * useful just before closing an old file that was opened * for read/write. */ #define trunc(stream) { \ fflush(stream); \ ftruncate(fileno(stream), (off_t)ftell(stream)); \ } /* * Use either alloca() or smalloc()/free(). ac_alloc can be used to * allocate space within a function. ac_free must be called when the * space is no longer needed, but expands to nothing if using alloca(). */ #ifdef HAVE_ALLOCA #define ac_alloc(n) alloca(n) #define ac_free(n) #else /* !HAVE_ALLOCA */ #define ac_alloc(n) smalloc(n) #define ac_free(n) free(n) #endif /* !HAVE_ALLOCA */ /* * glibc uses the slow thread-safe getc() even if _REENTRANT is not * defined. Work around it. */ #ifdef __GLIBC__ #undef getc #define getc(c) getc_unlocked(c) #undef putc #define putc(c, f) putc_unlocked(c, f) #undef putchar #define putchar(c) putc_unlocked((c), stdout) #endif /* __GLIBC__ */ #define CBAD (-15555) #define smin(a, b) ((a) < (b) ? (a) : (b)) #define smax(a, b) ((a) < (b) ? (b) : (a)) /* * For saving the current directory and later returning. */ #ifdef HAVE_FCHDIR struct cw { int cw_fd; }; #else /* !HAVE_FCHDIR */ struct cw { char cw_wd[PATHSIZE]; }; #endif /* !HAVE_FCHDIR */ #ifdef USE_SSL enum ssl_vrfy_level { VRFY_IGNORE, VRFY_WARN, VRFY_ASK, VRFY_STRICT }; #endif /* USE_SSL */ heirloom-mailx-12.5/dotlock.c000066400000000000000000000152261155563371200162100ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 1996 Christos Zoulas. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Christos Zoulas. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)dotlock.c 2.9 (gritter) 3/20/06"; #endif #endif #include "rcv.h" #include #include #include #include #include #include #include "extern.h" #ifndef O_SYNC #define O_SYNC 0 #endif static int maildir_access(const char *fname); static int perhaps_setgid(const char *name, gid_t gid); static int create_exclusive(const char *fname); /* Check if we can write a lock file at all */ static int maildir_access(const char *fname) { char *path; char *p; int i; path = ac_alloc(strlen(fname) + 2); strcpy(path, fname); p = strrchr(path, '/'); if (p != NULL) *p = '\0'; if (p == NULL || *path == '\0') strcpy(path, "."); i = access(path, R_OK|W_OK|X_OK); ac_free(path); return i; } /* * Set the gid if the path is in the normal mail spool */ static int perhaps_setgid(const char *name, gid_t gid) { char safepath[]= MAILSPOOL; if (strncmp(name, safepath, sizeof (safepath)-1) || strchr(name + sizeof (safepath), '/')) return 0; return (setgid (gid)); } #define APID_SZ 40 /* sufficient for storign 128 bits pids */ /* * Create a unique file. O_EXCL does not really work over NFS so we follow * the following trick: [Inspired by S.R. van den Berg] * * - make a mostly unique filename and try to create it. * - link the unique filename to our target * - get the link count of the target * - unlink the mostly unique filename * - if the link count was 2, then we are ok; else we've failed. */ static int create_exclusive(const char *fname) { char path[MAXPATHLEN]; char *hostname; char apid[APID_SZ]; const char *ptr; time_t t; pid_t pid; size_t ntries, cookie; int fd, serrno, cc; struct stat st; struct utsname ut; time(&t); uname(&ut); hostname = ut.nodename; pid = getpid(); cookie = (int)pid ^ (int)t; /* * We generate a semi-unique filename, from hostname.(pid ^ usec) */ if ((ptr = strrchr(fname, '/')) == NULL) ptr = fname; else ptr++; snprintf(path, sizeof path, "%.*s.%s.%x", (int) (ptr - fname), fname, hostname, (unsigned int) cookie); /* * We try to create the unique filename. */ for (ntries = 0; ntries < 5; ntries++) { perhaps_setgid(path, effectivegid); fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL|O_SYNC, 0); setgid(realgid); if (fd != -1) { snprintf(apid, APID_SZ, "%d", (int)getpid()); write(fd, apid, strlen(apid)); close(fd); break; } else if (errno == EEXIST) continue; else return -1; } /* * We link the path to the name */ perhaps_setgid(fname, effectivegid); cc = link(path, fname); setgid(realgid); if (cc == -1) goto bad; /* * Note that we stat our own exclusively created name, not the * destination, since the destination can be affected by others. */ if (stat(path, &st) == -1) goto bad; perhaps_setgid(fname, effectivegid); unlink(path); setgid(realgid); /* * If the number of links was two (one for the unique file and one * for the lock), we've won the race */ if (st.st_nlink != 2) { errno = EEXIST; return -1; } return 0; bad: serrno = errno; unlink(path); errno = serrno; return -1; } int fcntl_lock(int fd, int type) { struct flock flp; flp.l_type = type; flp.l_start = 0; flp.l_whence = SEEK_SET; flp.l_len = 0; return fcntl(fd, F_SETLKW, &flp); } int dot_lock(const char *fname, int fd, int pollinterval, FILE *fp, const char *msg) #ifdef notdef const char *fname; /* Pathname to lock */ int fd; /* File descriptor for fname, for fcntl lock */ int pollinterval; /* Interval to check for lock, -1 return */ FILE *fp; /* File to print message */ const char *msg; /* Message to print */ #endif { char path[MAXPATHLEN]; sigset_t nset, oset; int i, olderrno; if (maildir_access(fname) != 0) return 0; sigemptyset(&nset); sigaddset(&nset, SIGHUP); sigaddset(&nset, SIGINT); sigaddset(&nset, SIGQUIT); sigaddset(&nset, SIGTERM); sigaddset(&nset, SIGTTIN); sigaddset(&nset, SIGTTOU); sigaddset(&nset, SIGTSTP); sigaddset(&nset, SIGCHLD); snprintf(path, sizeof(path), "%s.lock", fname); for (i=0;i<15;i++) { sigprocmask(SIG_BLOCK, &nset, &oset); if (create_exclusive(path) != -1) { sigprocmask(SIG_SETMASK, &oset, NULL); return 0; } else { olderrno = errno; sigprocmask(SIG_SETMASK, &oset, NULL); } fcntl_lock(fd, F_UNLCK); if (olderrno != EEXIST) return -1; if (fp && msg) fputs(msg, fp); if (pollinterval) { if (pollinterval == -1) { errno = EEXIST; return -1; } sleep(pollinterval); } fcntl_lock(fd, F_WRLCK); } fprintf(stderr, catgets(catd, CATSET, 71, "%s seems a stale lock? Need to be removed by hand?\n"), path); return -1; } void dot_unlock(const char *fname) { char path[MAXPATHLEN]; if (maildir_access(fname) != 0) return; snprintf(path, sizeof(path), "%s.lock", fname); perhaps_setgid(path, effectivegid); unlink(path); setgid(realgid); } heirloom-mailx-12.5/edit.c000066400000000000000000000141471155563371200154770ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)edit.c 2.24 (gritter) 3/4/06"; #endif #endif /* not lint */ #include "rcv.h" #include "extern.h" #include #include /* * Mail -- a mail program * * Perform message editing functions. */ static int edit1(int *msgvec, int type); /* * Edit a message list. */ int editor(void *v) { int *msgvec = v; return edit1(msgvec, 'e'); } /* * Invoke the visual editor on a message list. */ int visual(void *v) { int *msgvec = v; return edit1(msgvec, 'v'); } /* * Edit a message by writing the message into a funnily-named file * (which should not exist) and forking an editor on it. * We get the editor from the stuff above. */ static int edit1(int *msgvec, int type) { int c; int i; FILE *fp = NULL; struct message *mp; off_t size; char *line = NULL; size_t linesize; int wb; /* * Deal with each message to be edited . . . */ wb = value("writebackedited") != NULL; for (i = 0; msgvec[i] && i < msgCount; i++) { sighandler_type sigint; if (i > 0) { char *p; printf(catgets(catd, CATSET, 72, "Edit message %d [ynq]? "), msgvec[i]); fflush(stdout); if (readline(stdin, &line, &linesize) < 0) break; for (p = line; blankchar(*p & 0377); p++); if (*p == 'q') break; if (*p == 'n') continue; } setdot(mp = &message[msgvec[i] - 1]); did_print_dot = 1; touch(mp); sigint = safe_signal(SIGINT, SIG_IGN); fp = run_editor(fp, mp->m_size, type, (mb.mb_perm & MB_EDIT) == 0 || !wb, NULL, mp, wb ? SEND_MBOX : SEND_TODISP_ALL, sigint); if (fp != NULL) { fseek(mb.mb_otf, 0L, SEEK_END); size = ftell(mb.mb_otf); mp->m_block = mailx_blockof(size); mp->m_offset = mailx_offsetof(size); mp->m_size = fsize(fp); mp->m_lines = 0; mp->m_flag |= MODIFY; rewind(fp); while ((c = getc(fp)) != EOF) { if (c == '\n') mp->m_lines++; if (putc(c, mb.mb_otf) == EOF) break; } if (ferror(mb.mb_otf)) perror("/tmp"); Fclose(fp); } safe_signal(SIGINT, sigint); } if (line) free(line); return 0; } /* * Run an editor on the file at "fpp" of "size" bytes, * and return a new file pointer. * Signals must be handled by the caller. * "Type" is 'e' for ed, 'v' for vi. */ FILE * run_editor(FILE *fp, off_t size, int type, int readonly, struct header *hp, struct message *mp, enum sendaction action, sighandler_type oldint) { FILE *nf = NULL; int t; time_t modtime; char *edit; struct stat statb; char *tempEdit; sigset_t set; if ((nf = Ftemp(&tempEdit, "Re", "w", readonly ? 0400 : 0600, 1)) == NULL) { perror(catgets(catd, CATSET, 73, "temporary mail edit file")); goto out; } if (hp) { t = GTO|GSUBJECT|GCC|GBCC|GNL|GCOMMA; if (hp->h_from || hp->h_replyto || hp->h_sender || hp->h_organization) t |= GIDENT; puthead(hp, nf, t, SEND_TODISP, CONV_NONE, NULL, NULL); } if (mp) { send(mp, nf, 0, NULL, action, NULL); } else { if (size >= 0) while (--size >= 0 && (t = getc(fp)) != EOF) putc(t, nf); else while ((t = getc(fp)) != EOF) putc(t, nf); } fflush(nf); if (fstat(fileno(nf), &statb) < 0) modtime = 0; else modtime = statb.st_mtime; if (ferror(nf)) { Fclose(nf); perror(tempEdit); unlink(tempEdit); Ftfree(&tempEdit); nf = NULL; goto out; } if (Fclose(nf) < 0) { perror(tempEdit); unlink(tempEdit); Ftfree(&tempEdit); nf = NULL; goto out; } nf = NULL; if ((edit = value(type == 'e' ? "EDITOR" : "VISUAL")) == NULL) edit = type == 'e' ? "ed" : "vi"; sigemptyset(&set); if (run_command(edit, oldint != SIG_IGN ? &set : NULL, -1, -1, tempEdit, NULL, NULL) < 0) { unlink(tempEdit); Ftfree(&tempEdit); goto out; } /* * If in read only mode or file unchanged, just remove the editor * temporary and return. */ if (readonly) { unlink(tempEdit); Ftfree(&tempEdit); goto out; } if (stat(tempEdit, &statb) < 0) { perror(tempEdit); Ftfree(&tempEdit); goto out; } if (modtime == statb.st_mtime) { unlink(tempEdit); Ftfree(&tempEdit); goto out; } /* * Now switch to new file. */ if ((nf = Fopen(tempEdit, "a+")) == NULL) perror(tempEdit); out: if (tempEdit) { unlink(tempEdit); Ftfree(&tempEdit); } return nf; } heirloom-mailx-12.5/extern.h000066400000000000000000000455741155563371200160740ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Sccsid @(#)extern.h 2.162 (gritter) 10/1/08 */ /* aux.c */ char *savestr(const char *str); char *save2str(const char *str, const char *old); char *savecat(const char *s1, const char *s2); void panic(const char *format, ...); void holdint(void); void relseint(void); void touch(struct message *mp); int is_dir(char *name); int argcount(char **argv); void i_strcpy(char *dest, const char *src, int size); char *i_strdup(const char *src); void makelow(char *cp); int substr(const char *str, const char *sub); char *colalign(const char *cp, int col, int fill); void try_pager(FILE *fp); int source(void *v); int unstack(void); void alter(char *name); int blankline(char *linebuf); int anyof(char *s1, char *s2); int is_prefix(const char *as1, const char *as2); char *last_at_before_slash(const char *sp); enum protocol which_protocol(const char *name); const char *protfile(const char *xcp); char *protbase(const char *cp); int disconnected(const char *file); unsigned pjw(const char *cp); long nextprime(long n); char *strenc(const char *cp); char *strdec(const char *cp); char *md5tohex(const void *vp); char *cram_md5_string(const char *user, const char *pass, const char *b64); char *getuser(void); char *getpassword(struct termios *otio, int *reset_tio, const char *query); void transflags(struct message *omessage, long omsgCount, int transparent); char *getrandstring(size_t length); void out_of_memory(void); void *smalloc(size_t s); void *srealloc(void *v, size_t s); void *scalloc(size_t nmemb, size_t size); char *sstpcpy(char *dst, const char *src); char *sstrdup(const char *cp); enum okay makedir(const char *name); enum okay cwget(struct cw *cw); enum okay cwret(struct cw *cw); void cwrelse(struct cw *cw); void makeprint(struct str *in, struct str *out); char *prstr(const char *s); int prout(const char *s, size_t sz, FILE *fp); int putuc(int u, int c, FILE *fp); int asccasecmp(const char *s1, const char *s2); int ascncasecmp(const char *s1, const char *s2, size_t sz); char *asccasestr(const char *haystack, const char *xneedle); /* base64.c */ char *strtob64(const char *p); char *memtob64(const void *vp, size_t isz); size_t mime_write_tob64(struct str *in, FILE *fo, int is_header); void mime_fromb64(struct str *in, struct str *out, int is_text); void mime_fromb64_b(struct str *in, struct str *out, int is_text, FILE *f); /* cache.c */ enum okay getcache1(struct mailbox *mp, struct message *m, enum needspec need, int setflags); enum okay getcache(struct mailbox *mp, struct message *m, enum needspec need); void putcache(struct mailbox *mp, struct message *m); void initcache(struct mailbox *mp); void purgecache(struct mailbox *mp, struct message *m, long mc); void delcache(struct mailbox *mp, struct message *m); enum okay cache_setptr(int transparent); enum okay cache_list(struct mailbox *mp, const char *base, int strip, FILE *fp); enum okay cache_remove(const char *name); enum okay cache_rename(const char *old, const char *new); unsigned long cached_uidvalidity(struct mailbox *mp); FILE *cache_queue(struct mailbox *mp); enum okay cache_dequeue(struct mailbox *mp); /* cmd1.c */ char *get_pager(void); int headers(void *v); int scroll(void *v); int Scroll(void *v); int screensize(void); int from(void *v); void printhead(int mesg, FILE *f, int threaded); int pdot(void *v); int pcmdlist(void *v); char *laststring(char *linebuf, int *flag, int strip); int more(void *v); int More(void *v); int type(void *v); int Type(void *v); int show(void *v); int pipecmd(void *v); int Pipecmd(void *v); int top(void *v); int stouch(void *v); int mboxit(void *v); int folders(void *v); /* cmd2.c */ int next(void *v); int save(void *v); int Save(void *v); int copycmd(void *v); int Copycmd(void *v); int cmove(void *v); int cMove(void *v); int cdecrypt(void *v); int cDecrypt(void *v); int cwrite(void *v); int delete(void *v); int deltype(void *v); int undeletecmd(void *v); int retfield(void *v); int igfield(void *v); int saveretfield(void *v); int saveigfield(void *v); int fwdretfield(void *v); int fwdigfield(void *v); int unignore(void *v); int unretain(void *v); int unsaveignore(void *v); int unsaveretain(void *v); int unfwdignore(void *v); int unfwdretain(void *v); /* cmd3.c */ int shell(void *v); int dosh(void *v); int help(void *v); int schdir(void *v); int respond(void *v); int respondall(void *v); int respondsender(void *v); int followup(void *v); int followupall(void *v); int followupsender(void *v); int preserve(void *v); int unread(void *v); int seen(void *v); int messize(void *v); int rexit(void *v); int set(void *v); int unset(void *v); int group(void *v); int ungroup(void *v); int cfile(void *v); int echo(void *v); int Respond(void *v); int Followup(void *v); int forwardcmd(void *v); int Forwardcmd(void *v); int ifcmd(void *v); int elsecmd(void *v); int endifcmd(void *v); int alternates(void *v); int resendcmd(void *v); int Resendcmd(void *v); int newmail(void *v); int shortcut(void *v); struct shortcut *get_shortcut(const char *str); int unshortcut(void *v); struct oldaccount *get_oldaccount(const char *name); int account(void *v); int cflag(void *v); int cunflag(void *v); int canswered(void *v); int cunanswered(void *v); int cdraft(void *v); int cundraft(void *v); int ckill(void *v); int cunkill(void *v); int cscore(void *v); int cnoop(void *v); int cremove(void *v); int crename(void *v); /* cmdtab.c */ /* collect.c */ struct attachment *edit_attachments(struct attachment *attach); struct attachment *add_attachment(struct attachment *attach, const char *file); FILE *collect(struct header *hp, int printheaders, struct message *mp, char *quotefile, int doprefix, int tflag); void savedeadletter(FILE *fp); /* dotlock.c */ int fcntl_lock(int fd, int type); int dot_lock(const char *fname, int fd, int pollinterval, FILE *fp, const char *msg); void dot_unlock(const char *fname); /* edit.c */ int editor(void *v); int visual(void *v); FILE *run_editor(FILE *fp, off_t size, int type, int readonly, struct header *hp, struct message *mp, enum sendaction action, sighandler_type oldint); /* fio.c */ void setptr(FILE *ibuf, off_t offset); int putline(FILE *obuf, char *linebuf, size_t count); #define readline(a, b, c) readline_restart(a, b, c, 0) int readline_restart(FILE *ibuf, char **linebuf, size_t *linesize, size_t n); FILE *setinput(struct mailbox *mp, struct message *m, enum needspec need); struct message *setdot(struct message *mp); int rm(char *name); void holdsigs(void); void relsesigs(void); off_t fsize(FILE *iob); char *expand(char *name); int getfold(char *name, int size); char *getdeadletter(void); char *fgetline(char **line, size_t *linesize, size_t *count, size_t *llen, FILE *fp, int appendnl); void newline_appended(void); enum okay get_body(struct message *mp); int sclose(struct sock *sp); enum okay swrite(struct sock *sp, const char *data); enum okay swrite1(struct sock *sp, const char *data, int sz, int use_buffer); int sgetline(char **line, size_t *linesize, size_t *linelen, struct sock *sp); enum okay sopen(const char *xserver, struct sock *sp, int use_ssl, const char *uhp, const char *portstr, int verbose); /* getname.c */ char *getname(int uid); int getuserid(char *name); /* getopt.c */ int getopt(int argc, char *const argv[], const char *optstring); /* head.c */ int is_head(char *linebuf, size_t linelen); void parse(char *line, size_t linelen, struct headline *hl, char *pbuf); void extract_header(FILE *fp, struct header *hp); #define hfield(a, b) hfield_mult(a, b, 1) char *hfield_mult(char *field, struct message *mp, int mult); char *thisfield(const char *linebuf, const char *field); char *nameof(struct message *mp, int reptype); char *skip_comment(const char *cp); char *routeaddr(const char *name); char *skin(char *name); char *realname(char *name); char *name1(struct message *mp, int reptype); int msgidcmp(const char *s1, const char *s2); int is_ign(char *field, size_t fieldlen, struct ignoretab ignore[2]); int member(char *realfield, struct ignoretab *table); char *fakefrom(struct message *mp); char *fakedate(time_t t); char *nexttoken(char *cp); time_t unixtime(char *from); time_t rfctime(char *date); time_t combinetime(int year, int month, int day, int hour, int minute, int second); void substdate(struct message *m); int check_from_and_sender(struct name *fromfield, struct name *senderfield); char *getsender(struct message *m); /* imap.c */ enum okay imap_noop(void); enum okay imap_select(struct mailbox *mp, off_t *size, int *count, const char *mbx); int imap_setfile(const char *xserver, int newmail, int isedit); enum okay imap_header(struct message *m); enum okay imap_body(struct message *m); void imap_getheaders(int bot, int top); void imap_quit(void); enum okay imap_undelete(struct message *m, int n); enum okay imap_unread(struct message *m, int n); int imap_imap(void *vp); int imap_newmail(int autoinc); enum okay imap_append(const char *xserver, FILE *fp); void imap_folders(const char *name, int strip); enum okay imap_copy(struct message *m, int n, const char *name); enum okay imap_search1(const char *spec, int f); int imap_thisaccount(const char *cp); enum okay imap_remove(const char *name); enum okay imap_rename(const char *old, const char *new); enum okay imap_dequeue(struct mailbox *mp, FILE *fp); int cconnect(void *vp); int cdisconnect(void *vp); int ccache(void *vp); time_t imap_read_date_time(const char *cp); time_t imap_read_date(const char *cp); const char *imap_make_date_time(time_t t); char *imap_quotestr(const char *s); char *imap_unquotestr(const char *s); /* imap_gssapi.c */ /* imap_search.c */ enum okay imap_search(const char *spec, int f); /* junk.c */ int cgood(void *v); int cjunk(void *v); int cungood(void *v); int cunjunk(void *v); int cclassify(void *v); int cprobability(void *v); /* lex.c */ int setfile(char *name, int newmail); int newmailinfo(int omsgCount); void commands(void); int execute(char *linebuf, int contxt, size_t linesize); void setmsize(int sz); void onintr(int s); void announce(int printheaders); int newfileinfo(void); int getmdot(int newmail); int pversion(void *v); void load(char *name); void initbox(const char *name); /* list.c */ int getmsglist(char *buf, int *vector, int flags); int getrawlist(const char *line, size_t linesize, char **argv, int argc, int echolist); int first(int f, int m); void mark(int mesg, int f); /* lzw.c */ int zwrite(void *cookie, const char *wbp, int num); int zfree(void *cookie); int zread(void *cookie, char *rbp, int num); void *zalloc(FILE *fp); /* macro.c */ int cdefine(void *v); int define1(const char *name, int account); int cundef(void *v); int ccall(void *v); int callaccount(const char *name); int callhook(const char *name, int newmail); int listaccounts(FILE *fp); int cdefines(void *v); void delaccount(const char *name); /* maildir.c */ int maildir_setfile(const char *name, int newmail, int isedit); void maildir_quit(void); enum okay maildir_append(const char *name, FILE *fp); enum okay maildir_remove(const char *name); /* main.c */ int main(int argc, char *argv[]); /* mime.c */ int mime_name_invalid(char *name, int putmsg); struct name *checkaddrs(struct name *np); char *gettcharset(void); char *need_hdrconv(struct header *hp, enum gfield w); #ifdef HAVE_ICONV iconv_t iconv_open_ft(const char *tocode, const char *fromcode); #endif /* HAVE_ICONV */ enum mimeenc mime_getenc(char *h); int mime_getcontent(char *h); char *mime_getparam(char *param, char *h); char *mime_getboundary(char *h); char *mime_filecontent(char *name); int get_mime_convert(FILE *fp, char **contenttype, char **charset, enum mimeclean *isclean, int dosign); void mime_fromhdr(struct str *in, struct str *out, enum tdflags flags); char *mime_fromaddr(char *name); size_t prefixwrite(void *ptr, size_t size, size_t nmemb, FILE *f, char *prefix, size_t prefixlen); size_t mime_write(void *ptr, size_t size, FILE *f, enum conversion convert, enum tdflags dflags, char *prefix, size_t prefixlen, char **rest, size_t *restsize); /* names.c */ struct name *nalloc(char *str, enum gfield ntype); struct name *extract(char *line, enum gfield ntype); struct name *sextract(char *line, enum gfield ntype); char *detract(struct name *np, enum gfield ntype); struct name *outof(struct name *names, FILE *fo, struct header *hp); int is_fileaddr(char *name); struct name *usermap(struct name *names); struct name *cat(struct name *n1, struct name *n2); char **unpack(struct name *np); struct name *elide(struct name *names); int count(struct name *np); struct name *delete_alternates(struct name *np); int is_myname(char *name); /* nss.c */ #ifdef USE_NSS enum okay ssl_open(const char *server, struct sock *sp, const char *uhp); void nss_gen_err(const char *fmt, ...); #endif /* USE_NSS */ /* openssl.c */ #ifdef USE_OPENSSL enum okay ssl_open(const char *server, struct sock *sp, const char *uhp); void ssl_gen_err(const char *fmt, ...); #endif /* USE_OPENSSL */ int cverify(void *vp); FILE *smime_sign(FILE *ip, struct header *); FILE *smime_encrypt(FILE *ip, const char *certfile, const char *to); struct message *smime_decrypt(struct message *m, const char *to, const char *cc, int signcall); enum okay smime_certsave(struct message *m, int n, FILE *op); /* pop3.c */ enum okay pop3_noop(void); int pop3_setfile(const char *server, int newmail, int isedit); enum okay pop3_header(struct message *m); enum okay pop3_body(struct message *m); void pop3_quit(void); /* popen.c */ sighandler_type safe_signal(int signum, sighandler_type handler); FILE *safe_fopen(const char *file, const char *mode, int *omode); FILE *Fopen(const char *file, const char *mode); FILE *Fdopen(int fd, const char *mode); int Fclose(FILE *fp); FILE *Zopen(const char *file, const char *mode, int *compression); FILE *Popen(const char *cmd, const char *mode, const char *shell, int newfd1); int Pclose(FILE *ptr); void close_all_files(void); int run_command(char *cmd, sigset_t *mask, int infd, int outfd, char *a0, char *a1, char *a2); int start_command(const char *cmd, sigset_t *mask, int infd, int outfd, const char *a0, const char *a1, const char *a2); void prepare_child(sigset_t *nset, int infd, int outfd); void sigchild(int signo); void free_child(int pid); int wait_child(int pid); /* quit.c */ int quitcmd(void *v); void quit(void); int holdbits(void); enum okay makembox(void); int savequitflags(void); void restorequitflags(int); /* send.c */ char *foldergets(char **s, size_t *size, size_t *count, size_t *llen, FILE *stream); #undef send #define send(a, b, c, d, e, f) xsend(a, b, c, d, e, f) int send(struct message *mp, FILE *obuf, struct ignoretab *doign, char *prefix, enum sendaction action, off_t *stats); /* sendout.c */ char *makeboundary(void); int mail(struct name *to, struct name *cc, struct name *bcc, struct name *smopts, char *subject, struct attachment *attach, char *quotefile, int recipient_record, int tflag, int Eflag); int sendmail(void *v); int Sendmail(void *v); enum okay mail1(struct header *hp, int printheaders, struct message *quote, char *quotefile, int recipient_record, int doprefix, int tflag, int Eflag); int mkdate(FILE *fo, const char *field); int puthead(struct header *hp, FILE *fo, enum gfield w, enum sendaction action, enum conversion convert, char *contenttype, char *charset); enum okay resend_msg(struct message *mp, struct name *to, int add_resent); /* smtp.c */ char *nodename(int mayoverride); char *myaddrs(struct header *hp); char *myorigin(struct header *hp); char *smtp_auth_var(const char *type, const char *addr); int smtp_mta(char *server, struct name *to, FILE *fi, struct header *hp, const char *user, const char *password, const char *skinned); /* ssl.c */ void ssl_set_vrfy_level(const char *uhp); enum okay ssl_vrfy_decide(void); char *ssl_method_string(const char *uhp); enum okay smime_split(FILE *ip, FILE **hp, FILE **bp, long xcount, int keep); FILE *smime_sign_assemble(FILE *hp, FILE *bp, FILE *sp); FILE *smime_encrypt_assemble(FILE *hp, FILE *yp); struct message *smime_decrypt_assemble(struct message *m, FILE *hp, FILE *bp); int ccertsave(void *v); enum okay rfc2595_hostname_match(const char *host, const char *pattern); /* strings.c */ void *salloc(size_t size); void *csalloc(size_t nmemb, size_t size); void sreset(void); void spreserve(void); /* temp.c */ FILE *Ftemp(char **fn, char *prefix, char *mode, int bits, int register_file); void Ftfree(char **fn); void tinit(void); /* thread.c */ int thread(void *vp); int unthread(void *vp); struct message *next_in_thread(struct message *mp); struct message *prev_in_thread(struct message *mp); struct message *this_in_thread(struct message *mp, long n); int sort(void *vp); int ccollapse(void *v); int cuncollapse(void *v); void uncollapse1(struct message *m, int always); /* tty.c */ int grabh(struct header *hp, enum gfield gflags, int subjfirst); char *readtty(char *prefix, char *string); int yorn(char *msg); /* v7.local.c */ void findmail(char *user, int force, char *buf, int size); void demail(void); char *username(void); /* vars.c */ void assign(const char *name, const char *value); char *vcopy(const char *str); char *value(const char *name); struct grouphead *findgroup(char *name); void printgroup(char *name); int hash(const char *name); int unset_internal(const char *name); void remove_group(const char *name); /* version.c */ heirloom-mailx-12.5/fio.c000066400000000000000000000635741155563371200153370ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)fio.c 2.76 (gritter) 9/16/09"; #endif #endif /* not lint */ #include "rcv.h" #include #include #include #ifdef HAVE_WORDEXP #include #endif /* HAVE_WORDEXP */ #include #if defined (USE_NSS) #include #include #elif defined (USE_OPENSSL) #include #include #include #include #include #endif /* USE_SSL */ #ifdef HAVE_SOCKETS #include #include #include #ifdef HAVE_ARPA_INET_H #include #endif /* HAVE_ARPA_INET_H */ #endif /* HAVE_SOCKETS */ #include #include "extern.h" /* * Mail -- a mail program * * File I/O. */ static void makemessage(void); static void append(struct message *mp); static char *globname(char *name); static size_t length_of_line(const char *line, size_t linesize); static char *fgetline_byone(char **line, size_t *linesize, size_t *llen, FILE *fp, int appendnl, size_t n); static enum okay get_header(struct message *mp); /* * Set up the input pointers while copying the mail file into /tmp. */ void setptr(FILE *ibuf, off_t offset) { int c; size_t count; char *cp, *cp2; struct message this; int maybe, inhead, thiscnt; char *linebuf = NULL; size_t linesize = 0, filesize; int broken_mbox = value("broken-mbox") != NULL; maybe = 1; inhead = 0; thiscnt = 0; memset(&this, 0, sizeof this); this.m_flag = MUSED|MNEW|MNEWEST; filesize = mailsize - offset; offset = ftell(mb.mb_otf); for (;;) { if (fgetline(&linebuf, &linesize, &filesize, &count, ibuf, 0) == NULL) { this.m_xsize = this.m_size; this.m_xlines = this.m_lines; this.m_have = HAVE_HEADER|HAVE_BODY; if (thiscnt > 0) append(&this); makemessage(); if (linebuf) free(linebuf); return; } #ifdef notdef if (linebuf[0] == '\0') linebuf[0] = '.'; #endif fwrite(linebuf, sizeof *linebuf, count, mb.mb_otf); if (ferror(mb.mb_otf)) { perror("/tmp"); exit(1); } if (linebuf[count - 1] == '\n') linebuf[count - 1] = '\0'; if (maybe && linebuf[0] == 'F' && is_head(linebuf, count)) { this.m_xsize = this.m_size; this.m_xlines = this.m_lines; this.m_have = HAVE_HEADER|HAVE_BODY; if (thiscnt++ > 0) append(&this); msgCount++; this.m_flag = MUSED|MNEW|MNEWEST; this.m_size = 0; this.m_lines = 0; this.m_block = mailx_blockof(offset); this.m_offset = mailx_offsetof(offset); inhead = 1; } else if (linebuf[0] == 0) { inhead = 0; } else if (inhead) { for (cp = linebuf, cp2 = "status";; cp++) { if ((c = *cp2++) == 0) { while (c = *cp++, whitechar(c)); if (cp[-1] != ':') break; while ((c = *cp++) != '\0') if (c == 'R') this.m_flag |= MREAD; else if (c == 'O') this.m_flag &= ~MNEW; break; } if (*cp != c && *cp != upperconv(c)) break; } for (cp = linebuf, cp2 = "x-status";; cp++) { if ((c = *cp2++) == 0) { while (c = *cp++, whitechar(c)); if (cp[-1] != ':') break; while ((c = *cp++) != '\0') if (c == 'F') this.m_flag |= MFLAGGED; else if (c == 'A') this.m_flag|=MANSWERED; else if (c == 'T') this.m_flag|=MDRAFTED; break; } if (*cp != c && *cp != upperconv(c)) break; } } offset += count; this.m_size += count; this.m_lines++; if (!broken_mbox) maybe = linebuf[0] == 0; } /*NOTREACHED*/ } /* * Drop the passed line onto the passed output buffer. * If a write error occurs, return -1, else the count of * characters written, including the newline. */ int putline(FILE *obuf, char *linebuf, size_t count) { fwrite(linebuf, sizeof *linebuf, count, obuf); putc('\n', obuf); if (ferror(obuf)) return (-1); return (count + 1); } /* * Read up a line from the specified input into the line * buffer. Return the number of characters read. Do not * include the newline at the end. * * n is the number of characters already read. */ int readline_restart(FILE *ibuf, char **linebuf, size_t *linesize, size_t n) { long sz; clearerr(ibuf); /* * Interrupts will cause trouble if we are inside a stdio call. As * this is only relevant if input comes from a terminal, we can simply * bypass it by read() then. */ if (fileno(ibuf) == 0 && is_a_tty[0]) { if (*linebuf == NULL || *linesize < LINESIZE + n + 1) *linebuf = srealloc(*linebuf, *linesize = LINESIZE + n + 1); for (;;) { if (n >= *linesize - 128) *linebuf = srealloc(*linebuf, *linesize += 256); again: sz = read(0, *linebuf + n, *linesize - n - 1); if (sz > 0) { n += sz; (*linebuf)[n] = '\0'; if (n > 0 && (*linebuf)[n - 1] == '\n') break; } else { if (sz < 0 && errno == EINTR) goto again; if (n > 0) { if ((*linebuf)[n - 1] != '\n') { (*linebuf)[n++] = '\n'; (*linebuf)[n] = '\0'; } break; } else return -1; } } } else { /* * Not reading from standard input or standard input not * a terminal. We read one char at a time as it is the * only way to get lines with embedded NUL characters in * standard stdio. */ if (fgetline_byone(linebuf, linesize, &n, ibuf, 1, n) == NULL) return -1; } if (n > 0 && (*linebuf)[n - 1] == '\n') (*linebuf)[--n] = '\0'; return n; } /* * Return a file buffer all ready to read up the * passed message pointer. */ FILE * setinput(struct mailbox *mp, struct message *m, enum needspec need) { enum okay ok = STOP; switch (need) { case NEED_HEADER: if (m->m_have & HAVE_HEADER) ok = OKAY; else ok = get_header(m); break; case NEED_BODY: if (m->m_have & HAVE_BODY) ok = OKAY; else ok = get_body(m); break; case NEED_UNSPEC: ok = OKAY; break; } if (ok != OKAY) return NULL; fflush(mp->mb_otf); if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block, m->m_offset), SEEK_SET) < 0) { perror("fseek"); panic(catgets(catd, CATSET, 77, "temporary file seek")); } return (mp->mb_itf); } struct message * setdot(struct message *mp) { if (dot != mp) { prevdot = dot; did_print_dot = 0; } dot = mp; uncollapse1(dot, 0); return dot; } /* * Take the data out of the passed ghost file and toss it into * a dynamically allocated message structure. */ static void makemessage(void) { if (msgCount == 0) append(NULL); setdot(message); message[msgCount].m_size = 0; message[msgCount].m_lines = 0; } /* * Append the passed message descriptor onto the message structure. */ static void append(struct message *mp) { if (msgCount + 1 >= msgspace) message = srealloc(message, (msgspace += 64) * sizeof *message); if (msgCount > 0) message[msgCount - 1] = *mp; } /* * Delete a file, but only if the file is a plain file. */ int rm(char *name) { struct stat sb; if (stat(name, &sb) < 0) return(-1); if (!S_ISREG(sb.st_mode)) { errno = EISDIR; return(-1); } return(unlink(name)); } static int sigdepth; /* depth of holdsigs() */ static sigset_t nset, oset; /* * Hold signals SIGHUP, SIGINT, and SIGQUIT. */ void holdsigs(void) { if (sigdepth++ == 0) { sigemptyset(&nset); sigaddset(&nset, SIGHUP); sigaddset(&nset, SIGINT); sigaddset(&nset, SIGQUIT); sigprocmask(SIG_BLOCK, &nset, &oset); } } /* * Release signals SIGHUP, SIGINT, and SIGQUIT. */ void relsesigs(void) { if (--sigdepth == 0) sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL); } /* * Determine the size of the file possessed by * the passed buffer. */ off_t fsize(FILE *iob) { struct stat sbuf; if (fstat(fileno(iob), &sbuf) < 0) return 0; return sbuf.st_size; } /* * Evaluate the string given as a new mailbox name. * Supported meta characters: * % for my system mail box * %user for user's system mail box * # for previous file * & invoker's mbox file * +file file in folder directory * any shell meta character * Return the file name as a dynamic string. */ char * expand(char *name) { char xname[PATHSIZE]; char foldbuf[PATHSIZE]; struct shortcut *sh; /* * The order of evaluation is "%" and "#" expand into constants. * "&" can expand into "+". "+" can expand into shell meta characters. * Shell meta characters expand into constants. * This way, we make no recursive expansion. */ if ((sh = get_shortcut(name)) != NULL) name = sh->sh_long; next: switch (*name) { case '%': if (name[1] == ':' && name[2]) { name = &name[2]; goto next; } findmail(name[1] ? name + 1 : myname, name[1] != '\0' || uflag, xname, sizeof xname); return savestr(xname); case '#': if (name[1] != 0) break; if (prevfile[0] == 0) { printf(catgets(catd, CATSET, 80, "No previous file\n")); return NULL; } return savestr(prevfile); case '&': if (name[1] == 0 && (name = value("MBOX")) == NULL) name = "~/mbox"; /* fall through */ } if (name[0] == '@' && which_protocol(mailname) == PROTO_IMAP) { snprintf(xname, sizeof xname, "%s/%s", protbase(mailname), &name[1]); name = savestr(xname); } if (name[0] == '+' && getfold(foldbuf, sizeof foldbuf) >= 0) { if (which_protocol(foldbuf) == PROTO_IMAP && strcmp(foldbuf, protbase(foldbuf))) snprintf(xname, sizeof xname, "%s%s", foldbuf, name+1); else snprintf(xname, sizeof xname, "%s/%s", foldbuf, name+1); name = savestr(xname); if (foldbuf[0] == '%' && foldbuf[1] == ':') goto next; } /* catch the most common shell meta character */ if (name[0] == '~' && (name[1] == '/' || name[1] == '\0')) { snprintf(xname, sizeof xname, "%s%s", homedir, name + 1); name = savestr(xname); } if (!anyof(name, "|&;<>~{}()[]*?$`'\"\\")) return name; if (which_protocol(name) == PROTO_FILE) return globname(name); else return name; } static char * globname(char *name) { #ifdef HAVE_WORDEXP wordexp_t we; char *cp; sigset_t nset; int i; /* * Some systems (notably Open UNIX 8.0.0) fork a shell for * wordexp() and wait for it; waiting will fail if our SIGCHLD * handler is active. */ sigemptyset(&nset); sigaddset(&nset, SIGCHLD); sigprocmask(SIG_BLOCK, &nset, NULL); i = wordexp(name, &we, 0); sigprocmask(SIG_UNBLOCK, &nset, NULL); switch (i) { case 0: break; case WRDE_NOSPACE: fprintf(stderr, catgets(catd, CATSET, 83, "\"%s\": Expansion buffer overflow.\n"), name); return NULL; case WRDE_BADCHAR: case WRDE_SYNTAX: default: fprintf(stderr, catgets(catd, CATSET, 242, "Syntax error in \"%s\"\n"), name); return NULL; } switch (we.we_wordc) { case 1: cp = savestr(we.we_wordv[0]); break; case 0: fprintf(stderr, catgets(catd, CATSET, 82, "\"%s\": No match.\n"), name); cp = NULL; break; default: fprintf(stderr, catgets(catd, CATSET, 84, "\"%s\": Ambiguous.\n"), name); cp = NULL; } wordfree(&we); return cp; #else /* !HAVE_WORDEXP */ char xname[PATHSIZE]; char cmdbuf[PATHSIZE]; /* also used for file names */ int pid, l; char *cp, *shell; int pivec[2]; extern int wait_status; struct stat sbuf; if (pipe(pivec) < 0) { perror("pipe"); return name; } snprintf(cmdbuf, sizeof cmdbuf, "echo %s", name); if ((shell = value("SHELL")) == NULL) shell = SHELL; pid = start_command(shell, 0, -1, pivec[1], "-c", cmdbuf, NULL); if (pid < 0) { close(pivec[0]); close(pivec[1]); return NULL; } close(pivec[1]); again: l = read(pivec[0], xname, sizeof xname); if (l < 0) { if (errno == EINTR) goto again; perror("read"); close(pivec[0]); return NULL; } close(pivec[0]); if (wait_child(pid) < 0 && WTERMSIG(wait_status) != SIGPIPE) { fprintf(stderr, catgets(catd, CATSET, 81, "\"%s\": Expansion failed.\n"), name); return NULL; } if (l == 0) { fprintf(stderr, catgets(catd, CATSET, 82, "\"%s\": No match.\n"), name); return NULL; } if (l == sizeof xname) { fprintf(stderr, catgets(catd, CATSET, 83, "\"%s\": Expansion buffer overflow.\n"), name); return NULL; } xname[l] = 0; for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--) ; cp[1] = '\0'; if (strchr(xname, ' ') && stat(xname, &sbuf) < 0) { fprintf(stderr, catgets(catd, CATSET, 84, "\"%s\": Ambiguous.\n"), name); return NULL; } return savestr(xname); #endif /* !HAVE_WORDEXP */ } /* * Determine the current folder directory name. */ int getfold(char *name, int size) { char *folder; enum protocol p; if ((folder = value("folder")) == NULL) return (-1); if (*folder == '/' || (p = which_protocol(folder)) != PROTO_FILE && p != PROTO_MAILDIR) { strncpy(name, folder, size); name[size-1]='\0'; } else { snprintf(name, size, "%s/%s", homedir, folder); } return (0); } /* * Return the name of the dead.letter file. */ char * getdeadletter(void) { char *cp; if ((cp = value("DEAD")) == NULL || (cp = expand(cp)) == NULL) cp = expand("~/dead.letter"); else if (*cp != '/') { char *buf; size_t sz; buf = ac_alloc(sz = strlen(cp) + 3); snprintf(buf, sz, "~/%s", cp); snprintf(buf, sz, "~/%s", cp); cp = expand(buf); ac_free(buf); } return cp; } /* * line is a buffer with the result of fgets(). Returns the first * newline or the last character read. */ static size_t length_of_line(const char *line, size_t linesize) { register size_t i; /* * Last character is always '\0' and was added by fgets. */ linesize--; for (i = 0; i < linesize; i++) if (line[i] == '\n') break; return i < linesize ? i + 1 : linesize; } /* * fgets replacement to handle lines of arbitrary size and with * embedded \0 characters. * line - line buffer. *line be NULL. * linesize - allocated size of line buffer. * count - maximum characters to read. May be NULL. * llen - length_of_line(*line). * fp - input FILE. * appendnl - always terminate line with \n, append if necessary. */ char * fgetline(char **line, size_t *linesize, size_t *count, size_t *llen, FILE *fp, int appendnl) { long i_llen, sz; if (count == NULL) /* * If we have no count, we cannot determine where the * characters returned by fgets() end if there was no * newline. We have to read one character at one. */ return fgetline_byone(line, linesize, llen, fp, appendnl, 0); if (*line == NULL || *linesize < LINESIZE) *line = srealloc(*line, *linesize = LINESIZE); sz = *linesize <= *count ? *linesize : *count + 1; if (sz <= 1 || fgets(*line, sz, fp) == NULL) /* * Leave llen untouched; it is used to determine whether * the last line was \n-terminated in some callers. */ return NULL; i_llen = length_of_line(*line, sz); *count -= i_llen; while ((*line)[i_llen - 1] != '\n') { *line = srealloc(*line, *linesize += 256); sz = *linesize - i_llen; sz = (sz <= *count ? sz : *count + 1); if (sz <= 1 || fgets(&(*line)[i_llen], sz, fp) == NULL) { if (appendnl) { (*line)[i_llen++] = '\n'; (*line)[i_llen] = '\0'; } break; } sz = length_of_line(&(*line)[i_llen], sz); i_llen += sz; *count -= sz; } if (llen) *llen = i_llen; return *line; } /* * Read a line, one character at once. */ static char * fgetline_byone(char **line, size_t *linesize, size_t *llen, FILE *fp, int appendnl, size_t n) { int c; if (*line == NULL || *linesize < LINESIZE + n + 1) *line = srealloc(*line, *linesize = LINESIZE + n + 1); for (;;) { if (n >= *linesize - 128) *line = srealloc(*line, *linesize += 256); c = getc(fp); if (c != EOF) { (*line)[n++] = c; (*line)[n] = '\0'; if (c == '\n') break; } else { if (n > 0) { if (appendnl) { (*line)[n++] = '\n'; (*line)[n] = '\0'; } break; } else return NULL; } } if (llen) *llen = n; return *line; } static enum okay get_header(struct message *mp) { switch (mb.mb_type) { case MB_FILE: case MB_MAILDIR: return OKAY; case MB_POP3: return pop3_header(mp); case MB_IMAP: case MB_CACHE: return imap_header(mp); case MB_VOID: return STOP; } /*NOTREACHED*/ return STOP; } enum okay get_body(struct message *mp) { switch (mb.mb_type) { case MB_FILE: case MB_MAILDIR: return OKAY; case MB_POP3: return pop3_body(mp); case MB_IMAP: case MB_CACHE: return imap_body(mp); case MB_VOID: return STOP; } /*NOTREACHED*/ return STOP; } #ifdef HAVE_SOCKETS static long xwrite(int fd, const char *data, size_t sz); static long xwrite(int fd, const char *data, size_t sz) { long wo, wt = 0; do { if ((wo = write(fd, data + wt, sz - wt)) < 0) { if (errno == EINTR) continue; else return -1; } wt += wo; } while (wt < sz); return sz; } int sclose(struct sock *sp) { int i; if (sp->s_fd > 0) { if (sp->s_onclose != NULL) (*sp->s_onclose)(); #if defined (USE_NSS) if (sp->s_use_ssl) { sp->s_use_ssl = 0; i = PR_Close(sp->s_prfd) == PR_SUCCESS ? 0 : -1; sp->s_prfd = NULL; } else #elif defined (USE_OPENSSL) if (sp->s_use_ssl) { sp->s_use_ssl = 0; SSL_shutdown(sp->s_ssl); SSL_free(sp->s_ssl); sp->s_ssl = NULL; SSL_CTX_free(sp->s_ctx); sp->s_ctx = NULL; } #endif /* USE_SSL */ { i = close(sp->s_fd); } sp->s_fd = -1; return i; } sp->s_fd = -1; return 0; } enum okay swrite(struct sock *sp, const char *data) { return swrite1(sp, data, strlen(data), 0); } enum okay swrite1(struct sock *sp, const char *data, int sz, int use_buffer) { int x; if (use_buffer > 0) { int di; enum okay ok; if (sp->s_wbuf == NULL) { sp->s_wbufsize = 4096; sp->s_wbuf = smalloc(sp->s_wbufsize); sp->s_wbufpos = 0; } while (sp->s_wbufpos + sz > sp->s_wbufsize) { di = sp->s_wbufsize - sp->s_wbufpos; sz -= di; if (sp->s_wbufpos > 0) { memcpy(&sp->s_wbuf[sp->s_wbufpos], data, di); ok = swrite1(sp, sp->s_wbuf, sp->s_wbufsize, -1); } else ok = swrite1(sp, data, sp->s_wbufsize, -1); if (ok != OKAY) return STOP; data += di; sp->s_wbufpos = 0; } if (sz == sp->s_wbufsize) { ok = swrite1(sp, data, sp->s_wbufsize, -1); if (ok != OKAY) return STOP; } else if (sz) { memcpy(&sp->s_wbuf[sp->s_wbufpos], data, sz); sp->s_wbufpos += sz; } return OKAY; } else if (use_buffer == 0 && sp->s_wbuf != NULL && sp->s_wbufpos > 0) { x = sp->s_wbufpos; sp->s_wbufpos = 0; if (swrite1(sp, sp->s_wbuf, x, -1) != OKAY) return STOP; } if (sz == 0) return OKAY; #if defined (USE_NSS) if (sp->s_use_ssl) { x = PR_Write(sp->s_prfd, data, sz); } else #elif defined (USE_OPENSSL) if (sp->s_use_ssl) { ssl_retry: x = SSL_write(sp->s_ssl, data, sz); if (x < 0) { switch (SSL_get_error(sp->s_ssl, x)) { case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: goto ssl_retry; } } } else #endif /* USE_SSL */ { x = xwrite(sp->s_fd, data, sz); } if (x != sz) { char o[512]; snprintf(o, sizeof o, "%s write error", sp->s_desc ? sp->s_desc : "socket"); #if defined (USE_NSS) sp->s_use_ssl ? nss_gen_err("%s", o) : perror(o); #elif defined (USE_OPENSSL) sp->s_use_ssl ? ssl_gen_err("%s", o) : perror(o); #else /* !USE_SSL */ perror(o); #endif /* !USE_SSL */ if (x < 0) sclose(sp); return STOP; } return OKAY; } int sgetline(char **line, size_t *linesize, size_t *linelen, struct sock *sp) { char *lp = *line; if (sp->s_rsz < 0) { sclose(sp); return sp->s_rsz; } do { if (*line == NULL || lp > &(*line)[*linesize - 128]) { size_t diff = lp - *line; *line = srealloc(*line, *linesize += 256); lp = &(*line)[diff]; } if (sp->s_rbufptr == NULL || sp->s_rbufptr >= &sp->s_rbuf[sp->s_rsz]) { #if defined (USE_NSS) if (sp->s_use_ssl) { if ((sp->s_rsz = PR_Read(sp->s_prfd, sp->s_rbuf, sizeof sp->s_rbuf)) <= 0) { if (sp->s_rsz < 0) { char o[512]; snprintf(o, sizeof o, "%s", sp->s_desc ? sp->s_desc : "socket"); nss_gen_err("%s", o); } break; } } else #elif defined (USE_OPENSSL) if (sp->s_use_ssl) { ssl_retry: if ((sp->s_rsz = SSL_read(sp->s_ssl, sp->s_rbuf, sizeof sp->s_rbuf)) <= 0) { if (sp->s_rsz < 0) { char o[512]; switch(SSL_get_error(sp->s_ssl, sp->s_rsz)) { case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: goto ssl_retry; } snprintf(o, sizeof o, "%s", sp->s_desc ? sp->s_desc : "socket"); ssl_gen_err("%s", o); } break; } } else #endif /* USE_SSL */ { again: if ((sp->s_rsz = read(sp->s_fd, sp->s_rbuf, sizeof sp->s_rbuf)) <= 0) { if (sp->s_rsz < 0) { char o[512]; if (errno == EINTR) goto again; snprintf(o, sizeof o, "%s", sp->s_desc ? sp->s_desc : "socket"); perror(o); } break; } } sp->s_rbufptr = sp->s_rbuf; } } while ((*lp++ = *sp->s_rbufptr++) != '\n'); *lp = '\0'; if (linelen) *linelen = lp - *line; return lp - *line; } enum okay sopen(const char *xserver, struct sock *sp, int use_ssl, const char *uhp, const char *portstr, int verbose) { #ifdef HAVE_IPv6_FUNCS char hbuf[NI_MAXHOST]; struct addrinfo hints, *res0, *res; #else /* !HAVE_IPv6_FUNCS */ struct sockaddr_in servaddr; struct in_addr **pptr; struct hostent *hp; struct servent *ep; unsigned short port = 0; #endif /* !HAVE_IPv6_FUNCS */ int sockfd; char *cp; char *server = (char *)xserver; if ((cp = strchr(server, ':')) != NULL) { portstr = &cp[1]; #ifndef HAVE_IPv6_FUNCS port = strtol(portstr, NULL, 10); #endif /* HAVE_IPv6_FUNCS */ server = salloc(cp - xserver + 1); memcpy(server, xserver, cp - xserver); server[cp - xserver] = '\0'; } #ifdef HAVE_IPv6_FUNCS memset(&hints, 0, sizeof hints); hints.ai_socktype = SOCK_STREAM; if (verbose) fprintf(stderr, "Resolving host %s . . .", server); if (getaddrinfo(server, portstr, &hints, &res0) != 0) { fprintf(stderr, catgets(catd, CATSET, 252, "Could not resolve host: %s\n"), server); return STOP; } else if (verbose) fprintf(stderr, " done.\n"); sockfd = -1; for (res = res0; res != NULL && sockfd < 0; res = res->ai_next) { if (verbose) { if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof hbuf, NULL, 0, NI_NUMERICHOST) != 0) strcpy(hbuf, "unknown host"); fprintf(stderr, catgets(catd, CATSET, 192, "Connecting to %s:%s . . ."), hbuf, portstr); } if ((sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) >= 0) { if (connect(sockfd, res->ai_addr, res->ai_addrlen)!=0) { close(sockfd); sockfd = -1; } } } if (sockfd < 0) { perror(catgets(catd, CATSET, 254, "could not connect")); freeaddrinfo(res0); return STOP; } freeaddrinfo(res0); #else /* !HAVE_IPv6_FUNCS */ if (port == 0) { if (equal(portstr, "smtp")) port = htons(25); else if (equal(portstr, "smtps")) port = htons(465); else if (equal(portstr, "imap")) port = htons(143); else if (equal(portstr, "imaps")) port = htons(993); else if (equal(portstr, "pop3")) port = htons(110); else if (equal(portstr, "pop3s")) port = htons(995); else if ((ep = getservbyname((char *)portstr, "tcp")) != NULL) port = ep->s_port; else { fprintf(stderr, catgets(catd, CATSET, 251, "Unknown service: %s\n"), portstr); return STOP; } } else port = htons(port); if (verbose) fprintf(stderr, "Resolving host %s . . .", server); if ((hp = gethostbyname(server)) == NULL) { fprintf(stderr, catgets(catd, CATSET, 252, "Could not resolve host: %s\n"), server); return STOP; } else if (verbose) fprintf(stderr, " done.\n"); pptr = (struct in_addr **)hp->h_addr_list; if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror(catgets(catd, CATSET, 253, "could not create socket")); return STOP; } memset(&servaddr, 0, sizeof servaddr); servaddr.sin_family = AF_INET; servaddr.sin_port = port; memcpy(&servaddr.sin_addr, *pptr, sizeof(struct in_addr)); if (verbose) fprintf(stderr, catgets(catd, CATSET, 192, "Connecting to %s:%d . . ."), inet_ntoa(**pptr), ntohs(port)); if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof servaddr) != 0) { perror(catgets(catd, CATSET, 254, "could not connect")); return STOP; } #endif /* !HAVE_IPv6_FUNCS */ if (verbose) fputs(catgets(catd, CATSET, 193, " connected.\n"), stderr); memset(sp, 0, sizeof *sp); sp->s_fd = sockfd; #if defined (USE_SSL) if (use_ssl) { enum okay ok; if ((ok = ssl_open(server, sp, uhp)) != OKAY) sclose(sp); return ok; } #endif /* USE_SSL */ return OKAY; } #endif /* HAVE_SOCKETS */ heirloom-mailx-12.5/getname.c000066400000000000000000000051501155563371200161640ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)getname.c 2.5 (gritter) 3/4/06"; #endif #endif /* not lint */ #include "rcv.h" #include #include "extern.h" /* Getname / getuserid for those with hashed passwd data base). */ /* * Search the passwd file for a uid. Return name on success, NULL on failure */ char * getname(int uid) { struct passwd *pw; if ((pw = getpwuid(uid)) == (struct passwd *)NULL) return NULL; return pw->pw_name; } /* * Convert the passed name to a user id and return it. Return -1 * on error. */ int getuserid(char *name) { struct passwd *pw; if ((pw = getpwnam(name)) == (struct passwd *)NULL) return -1; return pw->pw_uid; } heirloom-mailx-12.5/getopt.c000066400000000000000000000063371155563371200160560ustar00rootroot00000000000000/* * getopt() - command option parsing * * Gunnar Ritter, Freiburg i. Br., Germany, March 2002. */ /* Sccsid @(#)getopt.c 1.7 (gritter) 12/16/07 */ #include "config.h" #include #ifdef HAVE_ALLOCA #ifdef HAVE_ALLOCA_H #include #else /* !HAVE_ALLOCA_H */ #include #endif /* !HAVE_ALLOCA_H */ #endif /* HAVE_ALLOCA */ #include #ifdef HAVE_ALLOCA #define ac_alloc(n) alloca(n) #define ac_free(n) #else /* !HAVE_ALLOCA */ extern void *smalloc(size_t); #define ac_alloc(n) smalloc(n) #define ac_free(n) free(n) #endif /* !HAVE_ALLOCA */ #ifndef HAVE_SSIZE_T typedef int ssize_t; #endif /* !HAVE_SSIZE_T */ /* * One should not think that re-implementing this is necessary, but * * - Some libcs print weird messages. * * - GNU libc getopt() is totally brain-damaged, as it requires special * care _not_ to reorder parameters and can't be told to work correctly * with ':' as first optstring character at all. */ char *optarg; int optind = 1; int opterr = 1; int optopt; static void error(const char *s, int c) { /* * Avoid including , in case its getopt() declaration * conflicts. */ extern ssize_t write(int, const void *, size_t); const char *msg = 0; char *buf, *bp; switch (c) { case '?': msg = ": illegal option -- "; break; case ':': msg = ": option requires an argument -- "; break; } bp = buf = ac_alloc(strlen(s) + strlen(msg) + 2); while (*s) *bp++ = *s++; while (*msg) *bp++ = *msg++; *bp++ = optopt; *bp++ = '\n'; write(2, buf, bp - buf); ac_free(buf); } int getopt(int argc, char *const argv[], const char *optstring) { int colon; static const char *lastp; const char *curp; if (optstring[0] == ':') { colon = 1; optstring++; } else colon = 0; if (lastp) { curp = lastp; lastp = 0; } else { if (optind >= argc || argv[optind] == 0 || argv[optind][0] != '-' || argv[optind][1] == '\0') return -1; if (argv[optind][1] == '-' && argv[optind][2] == '\0') { optind++; return -1; } curp = &argv[optind][1]; } optopt = curp[0] & 0377; while (optstring[0]) { if (optstring[0] == ':') { optstring++; continue; } if ((optstring[0] & 0377) == optopt) { if (optstring[1] == ':') { if (curp[1] != '\0') { optarg = (char *)&curp[1]; optind++; } else { if ((optind += 2) > argc) { if (!colon && opterr) error(argv[0], ':'); return colon ? ':' : '?'; } optarg = argv[optind - 1]; } } else { if (curp[1] != '\0') lastp = &curp[1]; else optind++; optarg = 0; } return optopt; } optstring++; } if (!colon && opterr) error(argv[0], '?'); if (curp[1] != '\0') lastp = &curp[1]; else optind++; optarg = 0; return '?'; } #ifdef __APPLE__ /* * Starting with Mac OS 10.5 Leopard, turns getopt() * into getopt$UNIX2003() by default. Consequently, this function * is called instead of the one defined above. However, optind is * still taken from this file, so in effect, options are not * properly handled. Defining an own getopt$UNIX2003() function * works around this issue. */ int getopt$UNIX2003(int argc, char *const argv[], const char *optstring) { return getopt(argc, argv, optstring); } #endif /* __APPLE__ */ heirloom-mailx-12.5/glob.h000066400000000000000000000141331155563371200154750ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Sccsid @(#)glob.h 2.27 (gritter) 6/16/07 */ /* * A bunch of global variable declarations lie herein. * def.h must be included first. */ #if defined(_MAIL_GLOBS_) # undef _E # define _E #else # define _E extern #endif _E int msgCount; /* Count of messages read in */ _E int rcvmode; /* True if receiving mail */ _E int sawcom; /* Set after first command */ _E int Iflag; /* -I show Newsgroups: field */ _E char *Tflag; /* -T temp file for netnews */ _E int senderr; /* An error while checking */ _E int edit; /* Indicates editing a file */ _E int noreset; /* String resets suspended */ _E int sourcing; /* Currently reading variant file */ _E int loading; /* Loading user definitions */ _E enum condition cond; /* Current state of conditional exc. */ _E struct mailbox mb; /* Current mailbox */ _E int image; /* File descriptor for image of msg */ _E FILE *input; /* Current command input file */ _E char mailname[PATHSIZE]; /* Name of current file */ _E char mboxname[PATHSIZE]; /* Name of mbox */ _E char prevfile[PATHSIZE]; /* Name of previous file */ _E char *homedir; /* Path name of home directory */ _E char *progname; /* our name */ _E char *myname; /* My login name */ extern const char *version; /* version string */ _E off_t mailsize; /* Size of system mailbox */ _E struct message *dot; /* Pointer to current message */ _E struct message *prevdot; /* Previous current message */ _E struct message *message; /* The actual message structure */ _E struct message *threadroot; /* first threaded message */ _E int msgspace; /* Number of allocated struct m */ _E struct var *variables[HSHSIZE]; /* Pointer to active var list */ _E struct grouphead *groups[HSHSIZE]; /* Pointer to active groups */ _E struct ignoretab ignore[2]; /* ignored and retained fields 0 is ignore, 1 is retain */ _E struct ignoretab saveignore[2]; /* ignored and retained fields on save to folder */ _E struct ignoretab allignore[2]; /* special, ignore all headers */ _E struct ignoretab fwdignore[2]; /* fields to ignore for forwarding */ _E char **altnames; /* List of alternate names for user */ _E int debug; /* Debug flag set */ _E int scrnwidth; /* Screen width, or best guess */ _E int scrnheight; /* Screen height, or best guess, for "header" command */ _E int realscreenheight; /* the real screen height */ _E gid_t effectivegid; /* Saved from when we started up */ _E gid_t realgid; /* Saved from when we started up */ _E int exit_status; /* Exit status */ _E int is_a_tty[2]; /* isatty(0), isatty(1) */ _E int did_print_dot; /* current message has been printed */ _E int tildeflag; /* enable tilde escapes */ _E char *uflag; /* name given with -u option */ _E struct shortcut *shortcuts; /* list of shortcuts */ _E int mb_cur_max; /* value of MB_CUR_MAX */ _E int imap_created_mailbox; /* hack to get feedback from imap */ _E int unset_allow_undefined; /* allow to unset undefined variables */ _E int inhook; /* currently executing a hook */ _E int starting; /* still in startup code */ _E char *wantcharset; /* overrides the "charset" variable */ _E int utf8; /* UTF-8 encoding in use for locale */ _E int Rflag; /* open all folders read-only */ #ifdef USE_SSL _E enum ssl_vrfy_level ssl_vrfy_level; /* SSL verification level */ #endif #ifdef HAVE_ICONV _E iconv_t iconvd; #endif #ifdef HAVE_CATGETS _E nl_catd catd; #endif /* * These are initialized strings. */ extern char *us_ascii; /* "us-ascii" */ extern const char *month_names[]; #include _E sigjmp_buf srbuf; _E int interrupts; _E sighandler_type handlerstacktop; #define handlerpush(f) (savedtop = handlerstacktop, handlerstacktop = (f)) #define handlerpop() (handlerstacktop = savedtop) extern sighandler_type dflpipe; /* * The pointers for the string allocation routines, * there are NSPACE independent areas. * The first holds STRINGSIZE bytes, the next * twice as much, and so on. */ #define NSPACE 25 /* Total number of string spaces */ _E struct strings { char *s_topFree; /* Beginning of this area */ char *s_nextFree; /* Next alloctable place here */ unsigned s_nleft; /* Number of bytes left here */ } stringdope[NSPACE]; #undef _E heirloom-mailx-12.5/head.c000066400000000000000000000666751155563371200154700ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)head.c 2.17 (gritter) 3/4/06"; #endif #endif /* not lint */ #include "rcv.h" #include "extern.h" #include /* * Mail -- a mail program * * Routines for processing and detecting headlines. */ static char *copyin(char *src, char **space); static char *nextword(char *wp, char *wbuf); static int gethfield(FILE *f, char **linebuf, size_t *linesize, int rem, char **colon); static int msgidnextc(const char **cp, int *status); static int charcount(char *str, int c); /* * See if the passed line buffer is a mail header. * Return true if yes. POSIX.2 leaves the content * following 'From ' unspecified, so don't care about * it. */ /*ARGSUSED 2*/ int is_head(char *linebuf, size_t linelen) { char *cp; cp = linebuf; if (*cp++ != 'F' || *cp++ != 'r' || *cp++ != 'o' || *cp++ != 'm' || *cp++ != ' ') return (0); return(1); } /* * Split a headline into its useful components. * Copy the line into dynamic string space, then set * pointers into the copied line in the passed headline * structure. Actually, it scans. */ void parse(char *line, size_t linelen, struct headline *hl, char *pbuf) { char *cp; char *sp; char *word; hl->l_from = NULL; hl->l_tty = NULL; hl->l_date = NULL; cp = line; sp = pbuf; word = ac_alloc(linelen + 1); /* * Skip over "From" first. */ cp = nextword(cp, word); cp = nextword(cp, word); if (*word) hl->l_from = copyin(word, &sp); if (cp != NULL && cp[0] == 't' && cp[1] == 't' && cp[2] == 'y') { cp = nextword(cp, word); hl->l_tty = copyin(word, &sp); } if (cp != NULL) hl->l_date = copyin(cp, &sp); else hl->l_date = catgets(catd, CATSET, 213, ""); ac_free(word); } /* * Copy the string on the left into the string on the right * and bump the right (reference) string pointer by the length. * Thus, dynamically allocate space in the right string, copying * the left string into it. */ static char * copyin(char *src, char **space) { char *cp; char *top; top = cp = *space; while ((*cp++ = *src++) != '\0') ; *space = cp; return (top); } #ifdef notdef static int cmatch(char *, char *); /* * Test to see if the passed string is a ctime(3) generated * date string as documented in the manual. The template * below is used as the criterion of correctness. * Also, we check for a possible trailing time zone using * the tmztype template. */ /* * 'A' An upper case char * 'a' A lower case char * ' ' A space * '0' A digit * 'O' An optional digit or space * ':' A colon * '+' A sign * 'N' A new line */ static char *tmztype[] = { "Aaa Aaa O0 00:00:00 0000", "Aaa Aaa O0 00:00 0000", "Aaa Aaa O0 00:00:00 AAA 0000", "Aaa Aaa O0 00:00 AAA 0000", /* * Sommer time, e.g. MET DST */ "Aaa Aaa O0 00:00:00 AAA AAA 0000", "Aaa Aaa O0 00:00 AAA AAA 0000", /* * time zone offset, e.g. * +0200 or +0200 MET or +0200 MET DST */ "Aaa Aaa O0 00:00:00 +0000 0000", "Aaa Aaa O0 00:00 +0000 0000", "Aaa Aaa O0 00:00:00 +0000 AAA 0000", "Aaa Aaa O0 00:00 +0000 AAA 0000", "Aaa Aaa O0 00:00:00 +0000 AAA AAA 0000", "Aaa Aaa O0 00:00 +0000 AAA AAA 0000", /* * time zone offset without time zone specification (pine) */ "Aaa Aaa O0 00:00:00 0000 +0000", NULL, }; static int is_date(char *date) { int ret = 0, form = 0; while (tmztype[form]) { if ( (ret = cmatch(date, tmztype[form])) == 1 ) break; form++; } return ret; } /* * Match the given string (cp) against the given template (tp). * Return 1 if they match, 0 if they don't */ static int cmatch(char *cp, char *tp) { int c; while (*cp && *tp) switch (*tp++) { case 'a': if (c = *cp++, !lowerchar(c)) return 0; break; case 'A': if (c = *cp++, !upperchar(c)) return 0; break; case ' ': if (*cp++ != ' ') return 0; break; case '0': if (c = *cp++, !digitchar(c)) return 0; break; case 'O': if (c = *cp, c != ' ' && !digitchar(c)) return 0; cp++; break; case ':': if (*cp++ != ':') return 0; break; case '+': if (*cp != '+' && *cp != '-') return 0; cp++; break; case 'N': if (*cp++ != '\n') return 0; break; } if (*cp || *tp) return 0; return (1); } #endif /* notdef */ /* * Collect a liberal (space, tab delimited) word into the word buffer * passed. Also, return a pointer to the next word following that, * or NULL if none follow. */ static char * nextword(char *wp, char *wbuf) { int c; if (wp == NULL) { *wbuf = 0; return (NULL); } while ((c = *wp++) != '\0' && !blankchar(c)) { *wbuf++ = c; if (c == '"') { while ((c = *wp++) != '\0' && c != '"') *wbuf++ = c; if (c == '"') *wbuf++ = c; else wp--; } } *wbuf = '\0'; for (; blankchar(c); c = *wp++) ; if (c == 0) return (NULL); return (wp - 1); } void extract_header(FILE *fp, struct header *hp) { char *linebuf = NULL; size_t linesize = 0; int seenfields = 0; char *colon, *cp, *value; struct header nh; struct header *hq = &nh; int lc, c; memset(hq, 0, sizeof *hq); for (lc = 0; readline(fp, &linebuf, &linesize) > 0; lc++); rewind(fp); while ((lc = gethfield(fp, &linebuf, &linesize, lc, &colon)) >= 0) { if ((value = thisfield(linebuf, "to")) != NULL) { seenfields++; hq->h_to = checkaddrs(cat(hq->h_to, sextract(value, GTO|GFULL))); } else if ((value = thisfield(linebuf, "cc")) != NULL) { seenfields++; hq->h_cc = checkaddrs(cat(hq->h_cc, sextract(value, GCC|GFULL))); } else if ((value = thisfield(linebuf, "bcc")) != NULL) { seenfields++; hq->h_bcc = checkaddrs(cat(hq->h_bcc, sextract(value, GBCC|GFULL))); } else if ((value = thisfield(linebuf, "from")) != NULL) { seenfields++; hq->h_from = checkaddrs(cat(hq->h_from, sextract(value, GEXTRA|GFULL))); } else if ((value = thisfield(linebuf, "reply-to")) != NULL) { seenfields++; hq->h_replyto = checkaddrs(cat(hq->h_replyto, sextract(value, GEXTRA|GFULL))); } else if ((value = thisfield(linebuf, "sender")) != NULL) { seenfields++; hq->h_sender = checkaddrs(cat(hq->h_sender, sextract(value, GEXTRA|GFULL))); } else if ((value = thisfield(linebuf, "organization")) != NULL) { seenfields++; for (cp = value; blankchar(*cp & 0377); cp++); hq->h_organization = hq->h_organization ? save2str(hq->h_organization, cp) : savestr(cp); } else if ((value = thisfield(linebuf, "subject")) != NULL || (value = thisfield(linebuf, "subj")) != NULL) { seenfields++; for (cp = value; blankchar(*cp & 0377); cp++); hq->h_subject = hq->h_subject ? save2str(hq->h_subject, cp) : savestr(cp); } else fprintf(stderr, catgets(catd, CATSET, 266, "Ignoring header field \"%s\"\n"), linebuf); } /* * In case the blank line after the header has been edited out. * Otherwise, fetch the header separator. */ if (linebuf) { if (linebuf[0] != '\0') { for (cp = linebuf; *(++cp) != '\0'; ); fseek(fp, (long)-(1 + cp - linebuf), SEEK_CUR); } else { if ((c = getc(fp)) != '\n' && c != EOF) ungetc(c, fp); } } if (seenfields) { hp->h_to = hq->h_to; hp->h_cc = hq->h_cc; hp->h_bcc = hq->h_bcc; hp->h_from = hq->h_from; hp->h_replyto = hq->h_replyto; hp->h_sender = hq->h_sender; hp->h_organization = hq->h_organization; hp->h_subject = hq->h_subject; } else fprintf(stderr, catgets(catd, CATSET, 267, "Restoring deleted header lines\n")); if (linebuf) free(linebuf); } /* * Return the desired header line from the passed message * pointer (or NULL if the desired header field is not available). * If mult is zero, return the content of the first matching header * field only, the content of all matching header fields else. */ char * hfield_mult(char *field, struct message *mp, int mult) { FILE *ibuf; char *linebuf = NULL; size_t linesize = 0; int lc; char *hfield; char *colon, *oldhfield = NULL; if ((ibuf = setinput(&mb, mp, NEED_HEADER)) == NULL) return NULL; if ((lc = mp->m_lines - 1) < 0) return NULL; if ((mp->m_flag & MNOFROM) == 0) { if (readline(ibuf, &linebuf, &linesize) < 0) { if (linebuf) free(linebuf); return NULL; } } while (lc > 0) { if ((lc = gethfield(ibuf, &linebuf, &linesize, lc, &colon)) < 0) { if (linebuf) free(linebuf); return oldhfield; } if ((hfield = thisfield(linebuf, field)) != NULL) { oldhfield = save2str(hfield, oldhfield); if (mult == 0) break; } } if (linebuf) free(linebuf); return oldhfield; } /* * Return the next header field found in the given message. * Return >= 0 if something found, < 0 elsewise. * "colon" is set to point to the colon in the header. * Must deal with \ continuations & other such fraud. */ static int gethfield(FILE *f, char **linebuf, size_t *linesize, int rem, char **colon) { char *line2 = NULL; size_t line2size = 0; char *cp, *cp2; int c, isenc; if (*linebuf == NULL) *linebuf = srealloc(*linebuf, *linesize = 1); **linebuf = '\0'; for (;;) { if (--rem < 0) return -1; if ((c = readline(f, linebuf, linesize)) <= 0) return -1; for (cp = *linebuf; fieldnamechar(*cp & 0377); cp++); if (cp > *linebuf) while (blankchar(*cp & 0377)) cp++; if (*cp != ':' || cp == *linebuf) continue; /* * I guess we got a headline. * Handle wraparounding */ *colon = cp; cp = *linebuf + c; for (;;) { isenc = 0; while (--cp >= *linebuf && blankchar(*cp & 0377)); cp++; if (rem <= 0) break; if (cp-8 >= *linebuf && cp[-1] == '=' && cp[-2] == '?') isenc |= 1; ungetc(c = getc(f), f); if (!blankchar(c)) break; if ((c = readline(f, &line2, &line2size)) < 0) break; rem--; for (cp2 = line2; blankchar(*cp2 & 0377); cp2++); c -= cp2 - line2; if (cp2[0] == '=' && cp2[1] == '?' && c > 8) isenc |= 2; if (cp + c >= *linebuf + *linesize - 2) { size_t diff = cp - *linebuf; size_t colondiff = *colon - *linebuf; *linebuf = srealloc(*linebuf, *linesize += c + 2); cp = &(*linebuf)[diff]; *colon = &(*linebuf)[colondiff]; } if (isenc != 3) *cp++ = ' '; memcpy(cp, cp2, c); cp += c; } *cp = 0; if (line2) free(line2); return rem; } /* NOTREACHED */ } /* * Check whether the passed line is a header line of * the desired breed. Return the field body, or 0. */ char * thisfield(const char *linebuf, const char *field) { while (lowerconv(*linebuf&0377) == lowerconv(*field&0377)) { linebuf++; field++; } if (*field != '\0') return NULL; while (blankchar(*linebuf&0377)) linebuf++; if (*linebuf++ != ':') return NULL; while (blankchar(*linebuf&0377)) linebuf++; return (char *)linebuf; } /* * Get sender's name from this message. If the message has * a bunch of arpanet stuff in it, we may have to skin the name * before returning it. */ char * nameof(struct message *mp, int reptype) { char *cp, *cp2; cp = skin(name1(mp, reptype)); if (reptype != 0 || charcount(cp, '!') < 2) return(cp); cp2 = strrchr(cp, '!'); cp2--; while (cp2 > cp && *cp2 != '!') cp2--; if (*cp2 == '!') return(cp2 + 1); return(cp); } /* * Start of a "comment". * Ignore it. */ char * skip_comment(const char *cp) { int nesting = 1; for (; nesting > 0 && *cp; cp++) { switch (*cp) { case '\\': if (cp[1]) cp++; break; case '(': nesting++; break; case ')': nesting--; break; } } return (char *)cp; } /* * Return the start of a route-addr (address in angle brackets), * if present. */ char * routeaddr(const char *name) { const char *np, *rp = NULL; for (np = name; *np; np++) { switch (*np) { case '(': np = skip_comment(&np[1]) - 1; break; case '"': while (*np) { if (*++np == '"') break; if (*np == '\\' && np[1]) np++; } break; case '<': rp = np; break; case '>': return (char *)rp; } } return NULL; } /* * Skin an arpa net address according to the RFC 822 interpretation * of "host-phrase." */ char * skin(char *name) { int c; char *cp, *cp2; char *bufend; int gotlt, lastsp; char *nbuf; if (name == NULL) return(NULL); if (strchr(name, '(') == NULL && strchr(name, '<') == NULL && strchr(name, ' ') == NULL) return(name); gotlt = 0; lastsp = 0; nbuf = ac_alloc(strlen(name) + 1); bufend = nbuf; for (cp = name, cp2 = bufend; (c = *cp++) != '\0'; ) { switch (c) { case '(': cp = skip_comment(cp); lastsp = 0; break; case '"': /* * Start of a "quoted-string". * Copy it in its entirety. */ *cp2++ = c; while ((c = *cp) != '\0') { cp++; if (c == '"') { *cp2++ = c; break; } if (c != '\\') *cp2++ = c; else if ((c = *cp) != '\0') { *cp2++ = c; cp++; } } lastsp = 0; break; case ' ': if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ') cp += 3, *cp2++ = '@'; else if (cp[0] == '@' && cp[1] == ' ') cp += 2, *cp2++ = '@'; #if 0 /* * RFC 822 specifies spaces are STRIPPED when * in an adress specifier. */ else lastsp = 1; #endif break; case '<': cp2 = bufend; gotlt++; lastsp = 0; break; case '>': if (gotlt) { gotlt = 0; while ((c = *cp) != '\0' && c != ',') { cp++; if (c == '(') cp = skip_comment(cp); else if (c == '"') while ((c = *cp) != '\0') { cp++; if (c == '"') break; if (c == '\\' && *cp) cp++; } } lastsp = 0; break; } /* Fall into . . . */ default: if (lastsp) { lastsp = 0; *cp2++ = ' '; } *cp2++ = c; if (c == ',' && !gotlt) { *cp2++ = ' '; for (; *cp == ' '; cp++) ; lastsp = 0; bufend = cp2; } } } *cp2 = 0; cp = savestr(nbuf); ac_free(nbuf); return cp; } /* * Fetch the real name from an internet mail address field. */ char * realname(char *name) { char *cstart = NULL, *cend = NULL, *cp, *cq; char *rname, *rp; struct str in, out; int quoted, good, nogood; if (name == NULL) return NULL; for (cp = name; *cp; cp++) { switch (*cp) { case '(': if (cstart) /* * More than one comment in address, doesn't * make sense to display it without context. * Return the entire field, */ return mime_fromaddr(name); cstart = cp++; cp = skip_comment(cp); cend = cp--; if (cend <= cstart) cend = cstart = NULL; break; case '"': while (*cp) { if (*++cp == '"') break; if (*cp == '\\' && cp[1]) cp++; } break; case '<': if (cp > name) { cstart = name; cend = cp; } break; case ',': /* * More than one address. Just use the first one. */ goto brk; } } brk: if (cstart == NULL) { if (*name == '<') /* * If name contains only a route-addr, the * surrounding angle brackets don't serve any * useful purpose when displaying, so they * are removed. */ return prstr(skin(name)); return mime_fromaddr(name); } rp = rname = ac_alloc(cend - cstart + 1); /* * Strip quotes. Note that quotes that appear within a MIME- * encoded word are not stripped. The idea is to strip only * syntactical relevant things (but this is not necessarily * the most sensible way in practice). */ quoted = 0; for (cp = cstart; cp < cend; cp++) { if (*cp == '(' && !quoted) { cq = skip_comment(++cp); if (--cq > cend) cq = cend; while (cp < cq) { if (*cp == '\\' && &cp[1] < cq) cp++; *rp++ = *cp++; } } else if (*cp == '\\' && &cp[1] < cend) *rp++ = *++cp; else if (*cp == '"') { quoted = !quoted; continue; } else *rp++ = *cp; } *rp = '\0'; in.s = rname; in.l = rp - rname; mime_fromhdr(&in, &out, TD_ISPR|TD_ICONV); ac_free(rname); rname = savestr(out.s); free(out.s); while (blankchar(*rname & 0377)) rname++; for (rp = rname; *rp; rp++); while (--rp >= rname && blankchar(*rp & 0377)) *rp = '\0'; if (rp == rname) return mime_fromaddr(name); /* * mime_fromhdr() has converted all nonprintable characters to * question marks now. These and blanks are considered uninteresting; * if the displayed part of the real name contains more than 25% of * them, it is probably better to display the plain email address * instead. */ good = 0; nogood = 0; for (rp = rname; *rp && rp < &rname[20]; rp++) if (*rp == '?' || blankchar(*rp & 0377)) nogood++; else good++; if (good*3 < nogood) return prstr(skin(name)); return rname; } /* * Fetch the sender's name from the passed message. * Reptype can be * 0 -- get sender's name for display purposes * 1 -- get sender's name for reply * 2 -- get sender's name for Reply */ char * name1(struct message *mp, int reptype) { char *namebuf; size_t namesize; char *linebuf = NULL; size_t linesize = 0; char *cp, *cp2; FILE *ibuf; int first = 1; if ((cp = hfield("from", mp)) != NULL && *cp != '\0') return cp; if (reptype == 0 && (cp = hfield("sender", mp)) != NULL && *cp != '\0') return cp; namebuf = smalloc(namesize = 1); namebuf[0] = 0; if (mp->m_flag & MNOFROM) goto out; if ((ibuf = setinput(&mb, mp, NEED_HEADER)) == NULL) goto out; if (readline(ibuf, &linebuf, &linesize) < 0) goto out; newname: if (namesize <= linesize) namebuf = srealloc(namebuf, namesize = linesize + 1); for (cp = linebuf; *cp && *cp != ' '; cp++) ; for (; blankchar(*cp & 0377); cp++); for (cp2 = &namebuf[strlen(namebuf)]; *cp && !blankchar(*cp & 0377) && cp2 < namebuf + namesize - 1;) *cp2++ = *cp++; *cp2 = '\0'; if (readline(ibuf, &linebuf, &linesize) < 0) goto out; if ((cp = strchr(linebuf, 'F')) == NULL) goto out; if (strncmp(cp, "From", 4) != 0) goto out; if (namesize <= linesize) namebuf = srealloc(namebuf, namesize = linesize + 1); while ((cp = strchr(cp, 'r')) != NULL) { if (strncmp(cp, "remote", 6) == 0) { if ((cp = strchr(cp, 'f')) == NULL) break; if (strncmp(cp, "from", 4) != 0) break; if ((cp = strchr(cp, ' ')) == NULL) break; cp++; if (first) { strncpy(namebuf, cp, namesize); first = 0; } else { cp2=strrchr(namebuf, '!')+1; strncpy(cp2, cp, (namebuf+namesize)-cp2); } namebuf[namesize-2]='\0'; strcat(namebuf, "!"); goto newname; } cp++; } out: if (*namebuf != '\0' || ((cp = hfield("return-path", mp))) == NULL || *cp == '\0') cp = savestr(namebuf); if (linebuf) free(linebuf); free(namebuf); return cp; } static int msgidnextc(const char **cp, int *status) { int c; for (;;) { if (*status & 01) { if (**cp == '"') { *status &= ~01; (*cp)++; continue; } if (**cp == '\\') { (*cp)++; if (**cp == '\0') goto eof; } goto dfl; } switch (**cp) { case '(': *cp = skip_comment(&(*cp)[1]); continue; case '>': case '\0': eof: return '\0'; case '"': (*cp)++; *status |= 01; continue; case '@': *status |= 02; /*FALLTHRU*/ default: dfl: c = *(*cp)++ & 0377; return *status & 02 ? lowerconv(c) : c; } } } int msgidcmp(const char *s1, const char *s2) { int q1 = 0, q2 = 0; int c1, c2; do { c1 = msgidnextc(&s1, &q1); c2 = msgidnextc(&s2, &q2); if (c1 != c2) return c1 - c2; } while (c1 && c2); return c1 - c2; } /* * Count the occurances of c in str */ static int charcount(char *str, int c) { char *cp; int i; for (i = 0, cp = str; *cp; cp++) if (*cp == c) i++; return(i); } /* * See if the given header field is supposed to be ignored. */ int is_ign(char *field, size_t fieldlen, struct ignoretab ignore[2]) { char *realfld; int ret; if (ignore == NULL) return 0; if (ignore == allignore) return 1; /* * Lower-case the string, so that "Status" and "status" * will hash to the same place. */ realfld = ac_alloc(fieldlen + 1); i_strcpy(realfld, field, fieldlen + 1); if (ignore[1].i_count > 0) ret = !member(realfld, ignore + 1); else ret = member(realfld, ignore); ac_free(realfld); return ret; } int member(char *realfield, struct ignoretab *table) { struct ignore *igp; for (igp = table->i_head[hash(realfield)]; igp != 0; igp = igp->i_link) if (*igp->i_field == *realfield && equal(igp->i_field, realfield)) return (1); return (0); } /* * Fake Sender for From_ lines if missing, e. g. with POP3. */ char * fakefrom(struct message *mp) { char *name; if (((name = skin(hfield("return-path", mp))) == NULL || *name == '\0' ) && ((name = skin(hfield("from", mp))) == NULL || *name == '\0')) name = "-"; return name; } char * fakedate(time_t t) { char *cp, *cq; cp = ctime(&t); for (cq = cp; *cq && *cq != '\n'; cq++); *cq = '\0'; return savestr(cp); } char * nexttoken(char *cp) { for (;;) { if (*cp == '\0') return NULL; if (*cp == '(') { int nesting = 0; while (*cp != '\0') { switch (*cp++) { case '(': nesting++; break; case ')': nesting--; break; } if (nesting <= 0) break; } } else if (blankchar(*cp & 0377) || *cp == ',') cp++; else break; } return cp; } /* * From username Fri Jan 2 20:13:51 2004 * | | | | | * 0 5 10 15 20 */ time_t unixtime(char *from) { char *fp, *xp; time_t t; int i, year, month, day, hour, minute, second; int tzdiff; struct tm *tmptr; for (fp = from; *fp && *fp != '\n'; fp++); fp -= 24; if (fp - from < 7) goto invalid; if (fp[3] != ' ') goto invalid; for (i = 0; month_names[i]; i++) if (strncmp(&fp[4], month_names[i], 3) == 0) break; if (month_names[i] == 0) goto invalid; month = i + 1; if (fp[7] != ' ') goto invalid; day = strtol(&fp[8], &xp, 10); if (*xp != ' ' || xp != &fp[10]) goto invalid; hour = strtol(&fp[11], &xp, 10); if (*xp != ':' || xp != &fp[13]) goto invalid; minute = strtol(&fp[14], &xp, 10); if (*xp != ':' || xp != &fp[16]) goto invalid; second = strtol(&fp[17], &xp, 10); if (*xp != ' ' || xp != &fp[19]) goto invalid; year = strtol(&fp[20], &xp, 10); if (xp != &fp[24]) goto invalid; if ((t = combinetime(year, month, day, hour, minute, second)) == (time_t)-1) goto invalid; tzdiff = t - mktime(gmtime(&t)); tmptr = localtime(&t); if (tmptr->tm_isdst > 0) tzdiff += 3600; t -= tzdiff; return t; invalid: time(&t); return t; } time_t rfctime(char *date) { char *cp = date, *x; time_t t; int i, year, month, day, hour, minute, second; if ((cp = nexttoken(cp)) == NULL) goto invalid; if (alphachar(cp[0] & 0377) && alphachar(cp[1] & 0377) && alphachar(cp[2] & 0377) && cp[3] == ',') { if ((cp = nexttoken(&cp[4])) == NULL) goto invalid; } day = strtol(cp, &x, 10); if ((cp = nexttoken(x)) == NULL) goto invalid; for (i = 0; month_names[i]; i++) { if (strncmp(cp, month_names[i], 3) == 0) break; } if (month_names[i] == NULL) goto invalid; month = i + 1; if ((cp = nexttoken(&cp[3])) == NULL) goto invalid; year = strtol(cp, &x, 10); if ((cp = nexttoken(x)) == NULL) goto invalid; hour = strtol(cp, &x, 10); if (*x != ':') goto invalid; cp = &x[1]; minute = strtol(cp, &x, 10); if (*x == ':') { cp = &x[1]; second = strtol(cp, &x, 10); } else second = 0; if ((t = combinetime(year, month, day, hour, minute, second)) == (time_t)-1) goto invalid; if ((cp = nexttoken(x)) != NULL) { int sign = -1; char buf[3]; switch (*cp) { case '-': sign = 1; /*FALLTHRU*/ case '+': cp++; } if (digitchar(cp[0] & 0377) && digitchar(cp[1] & 0377) && digitchar(cp[2] & 0377) && digitchar(cp[3] & 0377)) { buf[2] = '\0'; buf[0] = cp[0]; buf[1] = cp[1]; t += strtol(buf, NULL, 10) * sign * 3600; buf[0] = cp[2]; buf[1] = cp[3]; t += strtol(buf, NULL, 10) * sign * 60; } } return t; invalid: return 0; } #define leapyear(year) ((year % 100 ? year : year / 100) % 4 == 0) time_t combinetime(int year, int month, int day, int hour, int minute, int second) { time_t t; if (second < 0 || minute < 0 || hour < 0 || day < 1) return -1; t = second + minute * 60 + hour * 3600 + (day - 1) * 86400; if (year < 70) year += 2000; else if (year < 1900) year += 1900; if (month > 1) t += 86400 * 31; if (month > 2) t += 86400 * (leapyear(year) ? 29 : 28); if (month > 3) t += 86400 * 31; if (month > 4) t += 86400 * 30; if (month > 5) t += 86400 * 31; if (month > 6) t += 86400 * 30; if (month > 7) t += 86400 * 31; if (month > 8) t += 86400 * 31; if (month > 9) t += 86400 * 30; if (month > 10) t += 86400 * 31; if (month > 11) t += 86400 * 30; year -= 1900; t += (year - 70) * 31536000 + ((year - 69) / 4) * 86400 - ((year - 1) / 100) * 86400 + ((year + 299) / 400) * 86400; return t; } void substdate(struct message *m) { char *cp; time_t now; /* * Determine the date to print in faked 'From ' lines. This is * traditionally the date the message was written to the mail * file. Try to determine this using RFC message header fields, * or fall back to current time. */ time(&now); if ((cp = hfield_mult("received", m, 0)) != NULL) { while ((cp = nexttoken(cp)) != NULL && *cp != ';') { do cp++; while (alnumchar(*cp & 0377)); } if (cp && *++cp) m->m_time = rfctime(cp); } if (m->m_time == 0 || m->m_time > now) if ((cp = hfield("date", m)) != NULL) m->m_time = rfctime(cp); if (m->m_time == 0 || m->m_time > now) m->m_time = now; } int check_from_and_sender(struct name *fromfield, struct name *senderfield) { if (fromfield && fromfield->n_flink && senderfield == NULL) { fprintf(stderr, "A Sender: field is required with multiple " "addresses in From: field.\n"); return 1; } if (senderfield && senderfield->n_flink) { fprintf(stderr, "The Sender: field may contain " "only one address.\n"); return 2; } return 0; } char * getsender(struct message *mp) { char *cp; struct name *np; if ((cp = hfield("from", mp)) == NULL || (np = sextract(cp, GEXTRA|GSKIN)) == NULL) return NULL; return np->n_flink != NULL ? skin(hfield("sender", mp)) : np->n_name; } heirloom-mailx-12.5/hmac.c000066400000000000000000000054371155563371200154640ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Derived from: Network Working Group H. Krawczyk Request for Comments: 2104 IBM Category: Informational M. Bellare UCSD R. Canetti IBM February 1997 HMAC: Keyed-Hashing for Message Authentication Status of This Memo This memo provides information for the Internet community. This memo does not specify an Internet standard of any kind. Distribution of this memo is unlimited. Appendix -- Sample Code For the sake of illustration we provide the following sample code for the implementation of HMAC-MD5 as well as some corresponding test vectors (the code is based on MD5 code as described in [MD5]). */ /* Sccsid @(#)hmac.c 1.8 (gritter) 3/4/06 */ #include "rcv.h" #include "md5.h" /* ** Function: hmac_md5 */ void hmac_md5 ( unsigned char *text, /* pointer to data stream */ int text_len, /* length of data stream */ unsigned char *key, /* pointer to authentication key */ int key_len, /* length of authentication key */ void *digest /* caller digest to be filled in */ ) { MD5_CTX context; unsigned char k_ipad[65]; /* inner padding - * key XORd with ipad */ unsigned char k_opad[65]; /* outer padding - * key XORd with opad */ unsigned char tk[16]; int i; /* if key is longer than 64 bytes reset it to key=MD5(key) */ if (key_len > 64) { MD5_CTX tctx; MD5Init(&tctx); MD5Update(&tctx, key, key_len); MD5Final(tk, &tctx); key = tk; key_len = 16; } /* * the HMAC_MD5 transform looks like: * * MD5(K XOR opad, MD5(K XOR ipad, text)) * * where K is an n byte key * ipad is the byte 0x36 repeated 64 times * opad is the byte 0x5c repeated 64 times * and text is the data being protected */ /* start out by storing key in pads */ memset(k_ipad, 0, sizeof k_ipad); memset(k_opad, 0, sizeof k_opad); memcpy(k_ipad, key, key_len); memcpy(k_opad, key, key_len); /* XOR key with ipad and opad values */ for (i=0; i<64; i++) { k_ipad[i] ^= 0x36; k_opad[i] ^= 0x5c; } /* * perform inner MD5 */ MD5Init(&context); /* init context for 1st * pass */ MD5Update(&context, k_ipad, 64); /* start with inner pad */ MD5Update(&context, text, text_len); /* then text of datagram */ MD5Final(digest, &context); /* finish up 1st pass */ /* * perform outer MD5 */ MD5Init(&context); /* init context for 2nd * pass */ MD5Update(&context, k_opad, 64); /* start with outer pad */ MD5Update(&context, digest, 16); /* then results of 1st * hash */ MD5Final(digest, &context); /* finish up 2nd pass */ } heirloom-mailx-12.5/imap.c000066400000000000000000002432061155563371200155000ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 2004 * Gunnar Ritter. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Gunnar Ritter * and his contributors. * 4. Neither the name of Gunnar Ritter nor the names of his contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)imap.c 1.222 (gritter) 3/13/09"; #endif #endif /* not lint */ #include "config.h" /* * Mail -- a mail program * * IMAP v4r1 client following RFC 2060. */ #include "rcv.h" #include #include #include #include #ifdef HAVE_SOCKETS #include "md5.h" #include #include #include #ifdef HAVE_ARPA_INET_H #include #endif /* HAVE_ARPA_INET_H */ #include "extern.h" static int verbose; #define IMAP_ANSWER() { \ if (mp->mb_type != MB_CACHE) { \ enum okay ok = OKAY; \ while (mp->mb_active & MB_COMD) \ ok = imap_answer(mp, 1); \ if (ok == STOP) \ return STOP; \ } \ } #define IMAP_OUT(x, y, action) \ { \ if (mp->mb_type != MB_CACHE) { \ if (imap_finish(mp) == STOP) \ return STOP; \ if (verbose) \ fprintf(stderr, ">>> %s", x); \ mp->mb_active |= (y); \ if (swrite(&mp->mb_sock, x) == STOP) \ action; \ } else { \ if (queuefp != NULL) \ fputs(x, queuefp); \ } \ } static struct record { struct record *rec_next; unsigned long rec_count; enum rec_type { REC_EXISTS, REC_EXPUNGE } rec_type; } *record, *recend; static enum { RESPONSE_TAGGED, RESPONSE_DATA, RESPONSE_FATAL, RESPONSE_CONT, RESPONSE_ILLEGAL } response_type; static enum { RESPONSE_OK, RESPONSE_NO, RESPONSE_BAD, RESPONSE_PREAUTH, RESPONSE_BYE, RESPONSE_OTHER, RESPONSE_UNKNOWN } response_status; static char *responded_tag; static char *responded_text; static char *responded_other_text; static long responded_other_number; static enum { MAILBOX_DATA_FLAGS, MAILBOX_DATA_LIST, MAILBOX_DATA_LSUB, MAILBOX_DATA_MAILBOX, MAILBOX_DATA_SEARCH, MAILBOX_DATA_STATUS, MAILBOX_DATA_EXISTS, MAILBOX_DATA_RECENT, MESSAGE_DATA_EXPUNGE, MESSAGE_DATA_FETCH, CAPABILITY_DATA, RESPONSE_OTHER_UNKNOWN } response_other; static enum list_attributes { LIST_NONE = 000, LIST_NOINFERIORS = 001, LIST_NOSELECT = 002, LIST_MARKED = 004, LIST_UNMARKED = 010 } list_attributes; static int list_hierarchy_delimiter; static char *list_name; struct list_item { struct list_item *l_next; char *l_name; char *l_base; enum list_attributes l_attr; int l_delim; int l_level; int l_has_children; }; static char *imapbuf; static size_t imapbufsize; static sigjmp_buf imapjmp; static sighandler_type savealrm; static int reset_tio; static struct termios otio; static int imapkeepalive; static long had_exists = -1; static long had_expunge = -1; static long expunged_messages; static volatile int imaplock; static int same_imap_account; static void imap_other_get(char *pp); static void imap_response_get(const char **cp); static void imap_response_parse(void); static enum okay imap_answer(struct mailbox *mp, int errprnt); static enum okay imap_parse_list(void); static enum okay imap_finish(struct mailbox *mp); static void imap_timer_off(void); static void imapcatch(int s); static void maincatch(int s); static enum okay imap_noop1(struct mailbox *mp); static void rec_queue(enum rec_type type, unsigned long count); static enum okay rec_dequeue(void); static void rec_rmqueue(void); static void imapalarm(int s); static int imap_use_starttls(const char *uhp); static enum okay imap_preauth(struct mailbox *mp, const char *xserver, const char *uhp); static enum okay imap_capability(struct mailbox *mp); static enum okay imap_auth(struct mailbox *mp, const char *uhp, char *xuser, const char *pass); static enum okay imap_cram_md5(struct mailbox *mp, char *xuser, const char *xpass); static enum okay imap_login(struct mailbox *mp, char *xuser, const char *xpass); #ifdef USE_GSSAPI static enum okay imap_gss(struct mailbox *mp, char *user); #endif /* USE_GSSAPI */ static enum okay imap_flags(struct mailbox *mp, unsigned X, unsigned Y); static void imap_init(struct mailbox *mp, int n); static void imap_setptr(struct mailbox *mp, int newmail, int transparent, int *prevcount); static char *imap_have_password(const char *server); static void imap_split(char **server, const char **sp, int *use_ssl, const char **cp, char **uhp, char **mbx, const char **pass, char **user); static int imap_setfile1(const char *xserver, int newmail, int isedit, int transparent); static int imap_fetchdata(struct mailbox *mp, struct message *m, size_t expected, int need, const char *head, size_t headsize, long headlines); static void imap_putstr(struct mailbox *mp, struct message *m, const char *str, const char *head, size_t headsize, long headlines); static enum okay imap_get(struct mailbox *mp, struct message *m, enum needspec need); static void commitmsg(struct mailbox *mp, struct message *to, struct message from, enum havespec have); static enum okay imap_fetchheaders(struct mailbox *mp, struct message *m, int bot, int top); static enum okay imap_exit(struct mailbox *mp); static enum okay imap_delete(struct mailbox *mp, int n, struct message *m, int needstat); static enum okay imap_close(struct mailbox *mp); static enum okay imap_update(struct mailbox *mp); static enum okay imap_store(struct mailbox *mp, struct message *m, int n, int c, const char *sp, int needstat); static enum okay imap_unstore(struct message *m, int n, const char *flag); static const char *tag(int new); static char *imap_putflags(int f); static void imap_getflags(const char *cp, char **xp, enum mflag *f); static enum okay imap_append1(struct mailbox *mp, const char *name, FILE *fp, off_t off1, long xsize, enum mflag flag, time_t t); static enum okay imap_append0(struct mailbox *mp, const char *name, FILE *fp); static enum okay imap_list1(struct mailbox *mp, const char *base, struct list_item **list, struct list_item **lend, int level); static enum okay imap_list(struct mailbox *mp, const char *base, int strip, FILE *fp); static void dopr(FILE *fp); static enum okay imap_copy1(struct mailbox *mp, struct message *m, int n, const char *name); static enum okay imap_copyuid_parse(const char *cp, unsigned long *uidvalidity, unsigned long *olduid, unsigned long *newuid); static enum okay imap_appenduid_parse(const char *cp, unsigned long *uidvalidity, unsigned long *uid); static enum okay imap_copyuid(struct mailbox *mp, struct message *m, const char *name); static enum okay imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1, long xsize, long size, long lines, int flag, const char *name); static enum okay imap_appenduid_cached(struct mailbox *mp, FILE *fp); static enum okay imap_search2(struct mailbox *mp, struct message *m, int count, const char *spec, int f); static enum okay imap_remove1(struct mailbox *mp, const char *name); static enum okay imap_rename1(struct mailbox *mp, const char *old, const char *new); static char *imap_strex(const char *cp, char **xp); static enum okay check_expunged(void); static void imap_other_get(char *pp) { char *xp; if (ascncasecmp(pp, "FLAGS ", 6) == 0) { pp += 6; response_other = MAILBOX_DATA_FLAGS; } else if (ascncasecmp(pp, "LIST ", 5) == 0) { pp += 5; response_other = MAILBOX_DATA_LIST; } else if (ascncasecmp(pp, "LSUB ", 5) == 0) { pp += 5; response_other = MAILBOX_DATA_LSUB; } else if (ascncasecmp(pp, "MAILBOX ", 8) == 0) { pp += 8; response_other = MAILBOX_DATA_MAILBOX; } else if (ascncasecmp(pp, "SEARCH ", 7) == 0) { pp += 7; response_other = MAILBOX_DATA_SEARCH; } else if (ascncasecmp(pp, "STATUS ", 7) == 0) { pp += 7; response_other = MAILBOX_DATA_STATUS; } else if (ascncasecmp(pp, "CAPABILITY ", 11) == 0) { pp += 11; response_other = CAPABILITY_DATA; } else { responded_other_number = strtol(pp, &xp, 10); while (*xp == ' ') xp++; if (ascncasecmp(xp, "EXISTS\r\n", 8) == 0) { response_other = MAILBOX_DATA_EXISTS; } else if (ascncasecmp(xp, "RECENT\r\n", 8) == 0) { response_other = MAILBOX_DATA_RECENT; } else if (ascncasecmp(xp, "EXPUNGE\r\n", 9) == 0) { response_other = MESSAGE_DATA_EXPUNGE; } else if (ascncasecmp(xp, "FETCH ", 6) == 0) { pp = &xp[6]; response_other = MESSAGE_DATA_FETCH; } else response_other = RESPONSE_OTHER_UNKNOWN; } responded_other_text = pp; } static void imap_response_get(const char **cp) { if (ascncasecmp(*cp, "OK ", 3) == 0) { *cp += 3; response_status = RESPONSE_OK; } else if (ascncasecmp(*cp, "NO ", 3) == 0) { *cp += 3; response_status = RESPONSE_NO; } else if (ascncasecmp(*cp, "BAD ", 4) == 0) { *cp += 4; response_status = RESPONSE_BAD; } else if (ascncasecmp(*cp, "PREAUTH ", 8) == 0) { *cp += 8; response_status = RESPONSE_PREAUTH; } else if (ascncasecmp(*cp, "BYE ", 4) == 0) { *cp += 4; response_status = RESPONSE_BYE; } else response_status = RESPONSE_OTHER; } static void imap_response_parse(void) { static char *parsebuf; static size_t parsebufsize; const char *ip = imapbuf; char *pp; if (parsebufsize < imapbufsize) { free(parsebuf); parsebuf = smalloc(parsebufsize = imapbufsize); } strcpy(parsebuf, imapbuf); pp = parsebuf; switch (*ip) { case '+': response_type = RESPONSE_CONT; ip++; pp++; while (*ip == ' ') { ip++; pp++; } break; case '*': ip++; pp++; while (*ip == ' ') { ip++; pp++; } imap_response_get(&ip); pp = &parsebuf[ip - imapbuf]; switch (response_status) { case RESPONSE_BYE: response_type = RESPONSE_FATAL; break; default: response_type = RESPONSE_DATA; } break; default: responded_tag = parsebuf; while (*pp && *pp != ' ') pp++; if (*pp == '\0') { response_type = RESPONSE_ILLEGAL; break; } *pp++ = '\0'; while (*pp && *pp == ' ') pp++; if (*pp == '\0') { response_type = RESPONSE_ILLEGAL; break; } ip = &imapbuf[pp - parsebuf]; response_type = RESPONSE_TAGGED; imap_response_get(&ip); pp = &parsebuf[ip - imapbuf]; } responded_text = pp; if (response_type != RESPONSE_CONT && response_type != RESPONSE_ILLEGAL && response_status == RESPONSE_OTHER) imap_other_get(pp); } static enum okay imap_answer(struct mailbox *mp, int errprnt) { int i, complete; enum okay ok = STOP; if (mp->mb_type == MB_CACHE) return OKAY; again: if (sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) { if (verbose) fputs(imapbuf, stderr); imap_response_parse(); if (response_type == RESPONSE_ILLEGAL) goto again; if (response_type == RESPONSE_CONT) return OKAY; if (response_status == RESPONSE_OTHER) { if (response_other == MAILBOX_DATA_EXISTS) { had_exists = responded_other_number; rec_queue(REC_EXISTS, responded_other_number); if (had_expunge > 0) had_expunge = 0; } else if (response_other == MESSAGE_DATA_EXPUNGE) { rec_queue(REC_EXPUNGE, responded_other_number); if (had_expunge < 0) had_expunge = 0; had_expunge++; expunged_messages++; } } complete = 0; if (response_type == RESPONSE_TAGGED) { if (asccasecmp(responded_tag, tag(0)) == 0) complete |= 1; else goto again; } switch (response_status) { case RESPONSE_PREAUTH: mp->mb_active &= ~MB_PREAUTH; /*FALLTHRU*/ case RESPONSE_OK: okay: ok = OKAY; complete |= 2; break; case RESPONSE_NO: case RESPONSE_BAD: stop: ok = STOP; complete |= 2; if (errprnt) fprintf(stderr, catgets(catd, CATSET, 218, "IMAP error: %s"), responded_text); break; case RESPONSE_UNKNOWN: /* does not happen */ case RESPONSE_BYE: i = mp->mb_active; mp->mb_active = MB_NONE; if (i & MB_BYE) goto okay; else goto stop; case RESPONSE_OTHER: ok = OKAY; } if (response_status != RESPONSE_OTHER && ascncasecmp(responded_text, "[ALERT] ", 8) == 0) fprintf(stderr, "IMAP alert: %s", &responded_text[8]); if (complete == 3) mp->mb_active &= ~MB_COMD; } else { ok = STOP; mp->mb_active = MB_NONE; } return ok; } static enum okay imap_parse_list(void) { char *cp; cp = responded_other_text; list_attributes = LIST_NONE; if (*cp == '(') { while (*cp && *cp != ')') { if (*cp == '\\') { if (ascncasecmp(&cp[1], "Noinferiors ", 12) == 0) { list_attributes |= LIST_NOINFERIORS; cp += 12; } else if (ascncasecmp(&cp[1], "Noselect ", 9) == 0) { list_attributes |= LIST_NOSELECT; cp += 9; } else if (ascncasecmp(&cp[1], "Marked ", 7) == 0) { list_attributes |= LIST_MARKED; cp += 7; } else if (ascncasecmp(&cp[1], "Unmarked ", 9) == 0) { list_attributes |= LIST_UNMARKED; cp += 9; } } cp++; } if (*++cp != ' ') return STOP; while (*cp == ' ') cp++; } list_hierarchy_delimiter = EOF; if (*cp == '"') { if (*++cp == '\\') cp++; list_hierarchy_delimiter = *cp++ & 0377; if (cp[0] != '"' || cp[1] != ' ') return STOP; cp++; } else if (cp[0] == 'N' && cp[1] == 'I' && cp[2] == 'L' && cp[3] == ' ') { list_hierarchy_delimiter = EOF; cp += 3; } while (*cp == ' ') cp++; list_name = cp; while (*cp && *cp != '\r') cp++; *cp = '\0'; return OKAY; } static enum okay imap_finish(struct mailbox *mp) { while (mp->mb_sock.s_fd > 0 && mp->mb_active & MB_COMD) imap_answer(mp, 1); return OKAY; } static void imap_timer_off(void) { if (imapkeepalive > 0) { alarm(0); safe_signal(SIGALRM, savealrm); } } static void imapcatch(int s) { if (reset_tio) tcsetattr(0, TCSADRAIN, &otio); switch (s) { case SIGINT: fprintf(stderr, catgets(catd, CATSET, 102, "Interrupt\n")); siglongjmp(imapjmp, 1); /*NOTREACHED*/ case SIGPIPE: fprintf(stderr, "Received SIGPIPE during IMAP operation\n"); break; } } static void maincatch(int s) { if (interrupts++ == 0) { fprintf(stderr, catgets(catd, CATSET, 102, "Interrupt\n")); return; } onintr(0); } static enum okay imap_noop1(struct mailbox *mp) { char o[LINESIZE]; FILE *queuefp = NULL; snprintf(o, sizeof o, "%s NOOP\r\n", tag(1)); IMAP_OUT(o, MB_COMD, return STOP) IMAP_ANSWER() return OKAY; } enum okay imap_noop(void) { sighandler_type saveint, savepipe; enum okay ok = STOP; (void)&saveint; (void)&savepipe; (void)&ok; if (mb.mb_type != MB_IMAP) return STOP; verbose = value("verbose") != NULL; imaplock = 1; if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN) safe_signal(SIGINT, maincatch); savepipe = safe_signal(SIGPIPE, SIG_IGN); if (sigsetjmp(imapjmp, 1) == 0) { if (savepipe != SIG_IGN) safe_signal(SIGPIPE, imapcatch); ok = imap_noop1(&mb); } safe_signal(SIGINT, saveint); safe_signal(SIGPIPE, savepipe); imaplock = 0; if (interrupts) onintr(0); return ok; } static void rec_queue(enum rec_type type, unsigned long count) { struct record *rp; rp = scalloc(1, sizeof *rp); rp->rec_type = type; rp->rec_count = count; if (record && recend) { recend->rec_next = rp; recend = rp; } else record = recend = rp; } static enum okay rec_dequeue(void) { struct message *omessage; enum okay ok = OKAY; struct record *rp = record, *rq = NULL; unsigned long exists = 0; long i; if (record == NULL) return STOP; omessage = message; message = smalloc((msgCount+1) * sizeof *message); if (msgCount) memcpy(message, omessage, msgCount * sizeof *message); memset(&message[msgCount], 0, sizeof *message); while (rp) { switch (rp->rec_type) { case REC_EXISTS: exists = rp->rec_count; break; case REC_EXPUNGE: if (rp->rec_count == 0) { ok = STOP; break; } if (rp->rec_count > msgCount) { if (exists == 0 || rp->rec_count > exists--) ok = STOP; break; } if (exists > 0) exists--; delcache(&mb, &message[rp->rec_count-1]); memmove(&message[rp->rec_count-1], &message[rp->rec_count], (msgCount - rp->rec_count + 1) * sizeof *message); msgCount--; /* * If the message was part of a collapsed thread, * the m_collapsed field of one of its ancestors * should be incremented. It seems hardly possible * to do this with the current message structure, * though. The result is that a '+' may be shown * in the header summary even if no collapsed * children exists. */ break; } free(rq); rq = rp; rp = rp->rec_next; } free(rq); record = recend = NULL; if (ok == OKAY && exists > msgCount) { message = srealloc(message, (exists + 1) * sizeof *message); memset(&message[msgCount], 0, (exists - msgCount + 1) * sizeof *message); for (i = msgCount; i < exists; i++) imap_init(&mb, i); imap_flags(&mb, msgCount+1, exists); msgCount = exists; } if (ok == STOP) { free(message); message = omessage; } return ok; } static void rec_rmqueue(void) { struct record *rp, *rq = NULL; for (rp = record; rp; rp = rp->rec_next) { free(rq); rq = rp; } free(rq); record = recend = NULL; } /*ARGSUSED*/ static void imapalarm(int s) { sighandler_type saveint; sighandler_type savepipe; if (imaplock++ == 0) { if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN) safe_signal(SIGINT, maincatch); savepipe = safe_signal(SIGPIPE, SIG_IGN); if (sigsetjmp(imapjmp, 1)) { safe_signal(SIGINT, saveint); safe_signal(SIGPIPE, savepipe); goto brk; } if (savepipe != SIG_IGN) safe_signal(SIGPIPE, imapcatch); if (imap_noop1(&mb) != OKAY) { safe_signal(SIGINT, saveint); safe_signal(SIGPIPE, savepipe); goto out; } safe_signal(SIGINT, saveint); safe_signal(SIGPIPE, savepipe); } brk: alarm(imapkeepalive); out: imaplock--; } static int imap_use_starttls(const char *uhp) { char *var; if (value("imap-use-starttls")) return 1; var = savecat("imap-use-starttls-", uhp); return value(var) != NULL; } static enum okay imap_preauth(struct mailbox *mp, const char *xserver, const char *uhp) { char *server, *cp; mp->mb_active |= MB_PREAUTH; imap_answer(mp, 1); if ((cp = strchr(xserver, ':')) != NULL) { server = salloc(cp - xserver + 1); memcpy(server, xserver, cp - xserver); server[cp - xserver] = '\0'; } else server = (char *)xserver; #ifdef USE_SSL if (mp->mb_sock.s_use_ssl == 0 && imap_use_starttls(uhp)) { FILE *queuefp = NULL; char o[LINESIZE]; snprintf(o, sizeof o, "%s STARTTLS\r\n", tag(1)); IMAP_OUT(o, MB_COMD, return STOP); IMAP_ANSWER() if (ssl_open(server, &mp->mb_sock, uhp) != OKAY) return STOP; } #else /* !USE_SSL */ if (imap_use_starttls(uhp)) { fprintf(stderr, "No SSL support compiled in.\n"); return STOP; } #endif /* !USE_SSL */ imap_capability(mp); return OKAY; } static enum okay imap_capability(struct mailbox *mp) { char o[LINESIZE]; FILE *queuefp = NULL; enum okay ok = STOP; const char *cp; snprintf(o, sizeof o, "%s CAPABILITY\r\n", tag(1)); IMAP_OUT(o, MB_COMD, return STOP) while (mp->mb_active & MB_COMD) { ok = imap_answer(mp, 0); if (response_status == RESPONSE_OTHER && response_other == CAPABILITY_DATA) { cp = responded_other_text; while (*cp) { while (spacechar(*cp&0377)) cp++; if (strncmp(cp, "UIDPLUS", 7) == 0 && spacechar(cp[7]&0377)) /* RFC 2359 */ mp->mb_flags |= MB_UIDPLUS; while (*cp && !spacechar(*cp&0377)) cp++; } } } return ok; } static enum okay imap_auth(struct mailbox *mp, const char *uhp, char *xuser, const char *pass) { char *var; char *auth; if (!(mp->mb_active & MB_PREAUTH)) return OKAY; if ((auth = value("imap-auth")) == NULL) { var = ac_alloc(strlen(uhp) + 11); strcpy(var, "imap-auth-"); strcpy(&var[10], uhp); auth = value(var); ac_free(var); } if (auth == NULL || strcmp(auth, "login") == 0) return imap_login(mp, xuser, pass); if (strcmp(auth, "cram-md5") == 0) return imap_cram_md5(mp, xuser, pass); if (strcmp(auth, "gssapi") == 0) { #ifdef USE_GSSAPI return imap_gss(mp, xuser); #else /* !USE_GSSAPI */ fprintf(stderr, "No GSSAPI support compiled in.\n"); return STOP; #endif /* !USE_GSSAPI */ } fprintf(stderr, "Unknown IMAP authentication method: \"%s\"\n", auth); return STOP; } /* * Implementation of RFC 2194. */ static enum okay imap_cram_md5(struct mailbox *mp, char *xuser, const char *xpass) { char o[LINESIZE]; const char *user, *pass; char *cp; FILE *queuefp = NULL; enum okay ok = STOP; retry: if (xuser == NULL) { if ((user = getuser()) == NULL) return STOP; } else user = xuser; if (xpass == NULL) { if ((pass = getpassword(&otio, &reset_tio, NULL)) == NULL) return STOP; } else pass = xpass; snprintf(o, sizeof o, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1)); IMAP_OUT(o, 0, return STOP) imap_answer(mp, 1); if (response_type != RESPONSE_CONT) return STOP; cp = cram_md5_string(user, pass, responded_text); IMAP_OUT(cp, MB_COMD, return STOP) while (mp->mb_active & MB_COMD) ok = imap_answer(mp, 1); if (ok == STOP) { xpass = NULL; goto retry; } return ok; } static enum okay imap_login(struct mailbox *mp, char *xuser, const char *xpass) { char o[LINESIZE]; const char *user, *pass; FILE *queuefp = NULL; enum okay ok = STOP; retry: if (xuser == NULL) { if ((user = getuser()) == NULL) return STOP; } else user = xuser; if (xpass == NULL) { if ((pass = getpassword(&otio, &reset_tio, NULL)) == NULL) return STOP; } else pass = xpass; snprintf(o, sizeof o, "%s LOGIN %s %s\r\n", tag(1), imap_quotestr(user), imap_quotestr(pass)); IMAP_OUT(o, MB_COMD, return STOP) while (mp->mb_active & MB_COMD) ok = imap_answer(mp, 1); if (ok == STOP) { xpass = NULL; goto retry; } return OKAY; } #ifdef USE_GSSAPI #include "imap_gssapi.c" #endif /* USE_GSSAPI */ enum okay imap_select(struct mailbox *mp, off_t *size, int *count, const char *mbx) { enum okay ok = OKAY; char *cp; char o[LINESIZE]; FILE *queuefp = NULL; mp->mb_uidvalidity = 0; snprintf(o, sizeof o, "%s SELECT %s\r\n", tag(1), imap_quotestr(mbx)); IMAP_OUT(o, MB_COMD, return STOP) while (mp->mb_active & MB_COMD) { ok = imap_answer(mp, 1); if (response_status != RESPONSE_OTHER && (cp = asccasestr(responded_text, "[UIDVALIDITY ")) != NULL) mp->mb_uidvalidity = atol(&cp[13]); } *count = had_exists > 0 ? had_exists : 0; if (response_status != RESPONSE_OTHER && ascncasecmp(responded_text, "[READ-ONLY] ", 12) == 0) mp->mb_perm = 0; return ok; } static enum okay imap_flags(struct mailbox *mp, unsigned X, unsigned Y) { char o[LINESIZE]; FILE *queuefp = NULL; char *cp; struct message *m; unsigned x = X, y = Y; int n; snprintf(o, sizeof o, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x, y); IMAP_OUT(o, MB_COMD, return STOP) while (mp->mb_active & MB_COMD) { imap_answer(mp, 1); if (response_status == RESPONSE_OTHER && response_other == MESSAGE_DATA_FETCH) { n = responded_other_number; if (n < x || n > y) continue; m = &message[n-1]; m->m_xsize = 0; } else continue; if ((cp = asccasestr(responded_other_text, "FLAGS ")) != NULL) { cp += 5; while (*cp == ' ') cp++; if (*cp == '(') imap_getflags(cp, &cp, &m->m_flag); } if ((cp = asccasestr(responded_other_text, "UID ")) != NULL) m->m_uid = strtoul(&cp[4], NULL, 10); getcache1(mp, m, NEED_UNSPEC, 1); m->m_flag &= ~MHIDDEN; } while (x <= y && message[x-1].m_xsize && message[x-1].m_time) x++; while (y > x && message[y-1].m_xsize && message[y-1].m_time) y--; if (x <= y) { snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n", tag(1), x, y); IMAP_OUT(o, MB_COMD, return STOP) while (mp->mb_active & MB_COMD) { imap_answer(mp, 1); if (response_status == RESPONSE_OTHER && response_other == MESSAGE_DATA_FETCH) { n = responded_other_number; if (n < x || n > y) continue; m = &message[n-1]; } else continue; if ((cp = asccasestr(responded_other_text, "RFC822.SIZE ")) != NULL) m->m_xsize = strtol(&cp[12], NULL, 10); if ((cp = asccasestr(responded_other_text, "INTERNALDATE ")) != NULL) m->m_time = imap_read_date_time(&cp[13]); } } for (n = X; n <= Y; n++) putcache(mp, &message[n-1]); return OKAY; } static void imap_init(struct mailbox *mp, int n) { struct message *m = &message[n]; m->m_flag = MUSED|MNOFROM; m->m_block = 0; m->m_offset = 0; } static void imap_setptr(struct mailbox *mp, int newmail, int transparent, int *prevcount) { struct message *omessage = 0; int i, omsgCount = 0; enum okay dequeued = STOP; if (newmail || transparent) { omessage = message; omsgCount = msgCount; } if (newmail) dequeued = rec_dequeue(); if (had_exists >= 0) { if (dequeued != OKAY) msgCount = had_exists; had_exists = -1; } if (had_expunge >= 0) { if (dequeued != OKAY) msgCount -= had_expunge; had_expunge = -1; } if (newmail && expunged_messages) printf("Expunged %ld message%s.\n", expunged_messages, expunged_messages != 1 ? "s" : ""); *prevcount = omsgCount - expunged_messages; expunged_messages = 0; if (msgCount < 0) { fputs("IMAP error: Negative message count\n", stderr); msgCount = 0; } if (dequeued != OKAY) { message = scalloc(msgCount + 1, sizeof *message); for (i = 0; i < msgCount; i++) imap_init(mp, i); if (!newmail && mp->mb_type == MB_IMAP) initcache(mp); if (msgCount > 0) imap_flags(mp, 1, msgCount); message[msgCount].m_size = 0; message[msgCount].m_lines = 0; rec_rmqueue(); } if (newmail || transparent) transflags(omessage, omsgCount, transparent); else setdot(message); } static char * imap_have_password(const char *server) { char *var, *cp; var = ac_alloc(strlen(server) + 10); strcpy(var, "password-"); strcpy(&var[9], server); if ((cp = value(var)) != NULL) cp = savestr(cp); ac_free(var); return cp; } static void imap_split(char **server, const char **sp, int *use_ssl, const char **cp, char **uhp, char **mbx, const char **pass, char **user) { *sp = *server; if (strncmp(*sp, "imap://", 7) == 0) { *sp = &(*sp)[7]; *use_ssl = 0; #ifdef USE_SSL } else if (strncmp(*sp, "imaps://", 8) == 0) { *sp = &(*sp)[8]; *use_ssl = 1; #endif /* USE_SSL */ } if ((*cp = strchr(*sp, '/')) != NULL && (*cp)[1] != '\0') { *uhp = savestr((char *)(*sp)); (*uhp)[*cp - *sp] = '\0'; *mbx = (char *)&(*cp)[1]; } else { if (*cp) (*server)[*cp - *server] = '\0'; *uhp = (char *)(*sp); *mbx = "INBOX"; } *pass = imap_have_password(*uhp); if ((*cp = last_at_before_slash(*uhp)) != NULL) { *user = salloc(*cp - *uhp + 1); memcpy(*user, *uhp, *cp - *uhp); (*user)[*cp - *uhp] = '\0'; *sp = &(*cp)[1]; *user = strdec(*user); } else { *user = NULL; *sp = *uhp; } } int imap_setfile(const char *xserver, int newmail, int isedit) { return imap_setfile1(xserver, newmail, isedit, 0); } static int imap_setfile1(const char *xserver, int newmail, int isedit, int transparent) { struct sock so; sighandler_type saveint; sighandler_type savepipe; char *server, *user, *account; const char *cp, *sp, *pass; char *uhp, *mbx; int use_ssl = 0; enum mbflags same_flags; int prevcount = 0; (void)&sp; (void)&use_ssl; (void)&saveint; (void)&savepipe; server = savestr((char *)xserver); verbose = value("verbose") != NULL; if (newmail) { saveint = safe_signal(SIGINT, SIG_IGN); savepipe = safe_signal(SIGPIPE, SIG_IGN); if (saveint != SIG_IGN) safe_signal(SIGINT, imapcatch); if (savepipe != SIG_IGN) safe_signal(SIGPIPE, imapcatch); imaplock = 1; goto newmail; } same_flags = mb.mb_flags; same_imap_account = 0; sp = protbase(server); if (mb.mb_imap_account) { if (mb.mb_sock.s_fd > 0 && strcmp(mb.mb_imap_account, sp) == 0 && disconnected(mb.mb_imap_account) == 0) same_imap_account = 1; } account = sstrdup(sp); imap_split(&server, &sp, &use_ssl, &cp, &uhp, &mbx, &pass, &user); so.s_fd = -1; if (!same_imap_account) { if (!disconnected(account) && sopen(sp, &so, use_ssl, uhp, use_ssl ? "imaps" : "imap", verbose) != OKAY) return -1; } else so = mb.mb_sock; if (!transparent) quit(); edit = isedit; free(mb.mb_imap_account); mb.mb_imap_account = account; if (!same_imap_account) { if (mb.mb_sock.s_fd >= 0) sclose(&mb.mb_sock); } same_imap_account = 0; if (!transparent) { if (mb.mb_itf) { fclose(mb.mb_itf); mb.mb_itf = NULL; } if (mb.mb_otf) { fclose(mb.mb_otf); mb.mb_otf = NULL; } free(mb.mb_imap_mailbox); mb.mb_imap_mailbox = sstrdup(mbx); initbox(server); } mb.mb_type = MB_VOID; mb.mb_active = MB_NONE;; imaplock = 1; saveint = safe_signal(SIGINT, SIG_IGN); savepipe = safe_signal(SIGPIPE, SIG_IGN); if (sigsetjmp(imapjmp, 1)) { sclose(&so); safe_signal(SIGINT, saveint); safe_signal(SIGPIPE, savepipe); imaplock = 0; return -1; } if (saveint != SIG_IGN) safe_signal(SIGINT, imapcatch); if (savepipe != SIG_IGN) safe_signal(SIGPIPE, imapcatch); if (mb.mb_sock.s_fd < 0) { if (disconnected(mb.mb_imap_account)) { if (cache_setptr(transparent) == STOP) fprintf(stderr, "Mailbox \"%s\" is not cached.\n", server); goto done; } if ((cp = value("imap-keepalive")) != NULL) { if ((imapkeepalive = strtol(cp, NULL, 10)) > 0) { savealrm = safe_signal(SIGALRM, imapalarm); alarm(imapkeepalive); } } mb.mb_sock = so; mb.mb_sock.s_desc = "IMAP"; mb.mb_sock.s_onclose = imap_timer_off; if (imap_preauth(&mb, sp, uhp) != OKAY || imap_auth(&mb, uhp, user, pass) != OKAY) { sclose(&mb.mb_sock); imap_timer_off(); safe_signal(SIGINT, saveint); safe_signal(SIGPIPE, savepipe); imaplock = 0; return -1; } } else /* same account */ mb.mb_flags |= same_flags; mb.mb_perm = Rflag ? 0 : MB_DELE; mb.mb_type = MB_IMAP; cache_dequeue(&mb); if (imap_select(&mb, &mailsize, &msgCount, mbx) != OKAY) { /*sclose(&mb.mb_sock); imap_timer_off();*/ safe_signal(SIGINT, saveint); safe_signal(SIGPIPE, savepipe); imaplock = 0; mb.mb_type = MB_VOID; return -1; } newmail: imap_setptr(&mb, newmail, transparent, &prevcount); done: setmsize(msgCount); if (!newmail && !transparent) sawcom = 0; safe_signal(SIGINT, saveint); safe_signal(SIGPIPE, savepipe); imaplock = 0; if (!newmail && mb.mb_type == MB_IMAP) purgecache(&mb, message, msgCount); if ((newmail || transparent) && mb.mb_sorted) { mb.mb_threaded = 0; sort((void *)-1); } if (!newmail && !edit && msgCount == 0) { if ((mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) && value("emptystart") == NULL) fprintf(stderr, catgets(catd, CATSET, 258, "No mail at %s\n"), server); return 1; } if (newmail) newmailinfo(prevcount); return 0; } static int imap_fetchdata(struct mailbox *mp, struct message *m, size_t expected, int need, const char *head, size_t headsize, long headlines) { char *line = NULL, *lp; size_t linesize = 0, linelen, size = 0; int emptyline = 0, lines = 0, excess = 0; off_t offset; fseek(mp->mb_otf, 0L, SEEK_END); offset = ftell(mp->mb_otf); if (head) fwrite(head, 1, headsize, mp->mb_otf); while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) { lp = line; if (linelen > expected) { excess = linelen - expected; linelen = expected; } /* * Need to mask 'From ' lines. This cannot be done properly * since some servers pass them as 'From ' and others as * '>From '. Although one could identify the first kind of * server in principle, it is not possible to identify the * second as '>From ' may also come from a server of the * first type as actual data. So do what is absolutely * necessary only - mask 'From '. * * If the line is the first line of the message header, it * is likely a real 'From ' line. In this case, it is just * ignored since it violates all standards. */ if (lp[0] == 'F' && lp[1] == 'r' && lp[2] == 'o' && lp[3] == 'm' && lp[4] == ' ') { if (lines + headlines != 0) { fputc('>', mp->mb_otf); size++; } else goto skip; } if (lp[linelen-1] == '\n' && (linelen == 1 || lp[linelen-2] == '\r')) { emptyline = linelen <= 2; if (linelen > 2) { fwrite(lp, 1, linelen - 2, mp->mb_otf); size += linelen - 1; } else size++; fputc('\n', mp->mb_otf); } else { emptyline = 0; fwrite(lp, 1, linelen, mp->mb_otf); size += linelen; } lines++; skip: if ((expected -= linelen) <= 0) break; } if (!emptyline) { /* * This is very ugly; but some IMAP daemons don't end a * message with \r\n\r\n, and we need \n\n for mbox format. */ fputc('\n', mp->mb_otf); lines++; size++; } fflush(mp->mb_otf); if (m != NULL) { m->m_size = size + headsize; m->m_lines = lines + headlines; m->m_block = mailx_blockof(offset); m->m_offset = mailx_offsetof(offset); switch (need) { case NEED_HEADER: m->m_have |= HAVE_HEADER; break; case NEED_BODY: m->m_have |= HAVE_HEADER|HAVE_BODY; m->m_xlines = m->m_lines; m->m_xsize = m->m_size; break; } } free(line); return excess; } static void imap_putstr(struct mailbox *mp, struct message *m, const char *str, const char *head, size_t headsize, long headlines) { off_t offset; size_t len; len = strlen(str); fseek(mp->mb_otf, 0L, SEEK_END); offset = ftell(mp->mb_otf); if (head) fwrite(head, 1, headsize, mp->mb_otf); if (len > 0) { fwrite(str, 1, len, mp->mb_otf); fputc('\n', mp->mb_otf); len++; } fflush(mp->mb_otf); if (m != NULL) { m->m_size = headsize + len; m->m_lines = headlines + 1; m->m_block = mailx_blockof(offset); m->m_offset = mailx_offsetof(offset); m->m_have |= HAVE_HEADER|HAVE_BODY; m->m_xlines = m->m_lines; m->m_xsize = m->m_size; } } static enum okay imap_get(struct mailbox *mp, struct message *m, enum needspec need) { sighandler_type saveint = SIG_IGN; sighandler_type savepipe = SIG_IGN; char o[LINESIZE], *cp = NULL, *item = NULL, *resp = NULL, *loc = NULL; size_t expected, headsize = 0; int number = m - message + 1; enum okay ok = STOP; FILE *queuefp = NULL; char *head = NULL; long headlines = 0; struct message mt; long n = -1; unsigned long u = 0; (void)&saveint; (void)&savepipe; (void)&number; (void)&need; (void)&cp; (void)&loc; (void)&ok; (void)&headsize; (void)&head; (void)&headlines; (void)&item; (void)&resp; (void)&u; verbose = value("verbose") != NULL; if (getcache(mp, m, need) == OKAY) return OKAY; if (mp->mb_type == MB_CACHE) { fprintf(stderr, "Message %u not available.\n", number); return STOP; } if (mp->mb_sock.s_fd < 0) { fprintf(stderr, "IMAP connection closed.\n"); return STOP; } switch (need) { case NEED_HEADER: resp = item = "RFC822.HEADER"; break; case NEED_BODY: item = "BODY.PEEK[]"; resp = "BODY[]"; if (m->m_flag & HAVE_HEADER && m->m_size) { char *hdr = smalloc(m->m_size); fflush(mp->mb_otf); if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block, m->m_offset), SEEK_SET) < 0 || fread(hdr, 1, m->m_size, mp->mb_itf) != m->m_size) { free(hdr); break; } head = hdr; headsize = m->m_size; headlines = m->m_lines; item = "BODY.PEEK[TEXT]"; resp = "BODY[TEXT]"; } break; case NEED_UNSPEC: return STOP; } imaplock = 1; savepipe = safe_signal(SIGPIPE, SIG_IGN); if (sigsetjmp(imapjmp, 1)) { safe_signal(SIGINT, saveint); safe_signal(SIGPIPE, savepipe); imaplock = 0; return STOP; } if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN) safe_signal(SIGINT, maincatch); if (savepipe != SIG_IGN) safe_signal(SIGPIPE, imapcatch); if (m->m_uid) snprintf(o, sizeof o, "%s UID FETCH %lu (%s)\r\n", tag(1), m->m_uid, item); else { if (check_expunged() == STOP) goto out; snprintf(o, sizeof o, "%s FETCH %u (%s)\r\n", tag(1), number, item); } IMAP_OUT(o, MB_COMD, goto out) for (;;) { ok = imap_answer(mp, 1); if (ok == STOP) break; if (response_status != RESPONSE_OTHER || response_other != MESSAGE_DATA_FETCH) continue; if ((loc = asccasestr(responded_other_text, resp)) == NULL) continue; if (m->m_uid) { if ((cp = asccasestr(responded_other_text, "UID "))) { u = atol(&cp[4]); n = 0; } else { n = -1; u = 0; } } else n = responded_other_number; if ((cp = strrchr(responded_other_text, '{')) == NULL) { if (m->m_uid ? m->m_uid != u : n != number) continue; if ((cp = strchr(loc, '"')) != NULL) { cp = imap_unquotestr(cp); imap_putstr(mp, m, cp, head, headsize, headlines); } else { m->m_have |= HAVE_HEADER|HAVE_BODY; m->m_xlines = m->m_lines; m->m_xsize = m->m_size; } goto out; } expected = atol(&cp[1]); if (m->m_uid ? n == 0 && m->m_uid != u : n != number) { imap_fetchdata(mp, NULL, expected, need, NULL, 0, 0); continue; } mt = *m; imap_fetchdata(mp, &mt, expected, need, head, headsize, headlines); if (n >= 0) { commitmsg(mp, m, mt, mt.m_have); break; } if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) { if (verbose) fputs(imapbuf, stderr); if ((cp = asccasestr(imapbuf, "UID ")) != NULL) { u = atol(&cp[4]); if (u == m->m_uid) { commitmsg(mp, m, mt, mt.m_have); break; } } } } out: while (mp->mb_active & MB_COMD) ok = imap_answer(mp, 1); if (saveint != SIG_IGN) safe_signal(SIGINT, saveint); if (savepipe != SIG_IGN) safe_signal(SIGPIPE, savepipe); imaplock--; if (ok == OKAY) putcache(mp, m); free(head); if (interrupts) onintr(0); return ok; } enum okay imap_header(struct message *m) { return imap_get(&mb, m, NEED_HEADER); } enum okay imap_body(struct message *m) { return imap_get(&mb, m, NEED_BODY); } static void commitmsg(struct mailbox *mp, struct message *to, struct message from, enum havespec have) { to->m_size = from.m_size; to->m_lines = from.m_lines; to->m_block = from.m_block; to->m_offset = from.m_offset; to->m_have = have; if (have & HAVE_BODY) { to->m_xlines = from.m_lines; to->m_xsize = from.m_size; } putcache(mp, to); } static enum okay imap_fetchheaders ( struct mailbox *mp, struct message *m, int bot, int top /* bot > top */ ) { char o[LINESIZE], *cp; struct message mt; size_t expected; enum okay ok; int n = 0, u; FILE *queuefp = NULL; if (m[bot].m_uid) snprintf(o, sizeof o, "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n", tag(1), m[bot-1].m_uid, m[top-1].m_uid); else { if (check_expunged() == STOP) return STOP; snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.HEADER)\r\n", tag(1), bot, top); } IMAP_OUT(o, MB_COMD, return STOP) for (;;) { ok = imap_answer(mp, 1); if (response_status != RESPONSE_OTHER) break; if (response_other != MESSAGE_DATA_FETCH) continue; if (ok == STOP || (cp=strrchr(responded_other_text, '{')) == 0) return STOP; if (asccasestr(responded_other_text, "RFC822.HEADER") == NULL) continue; expected = atol(&cp[1]); if (m[bot-1].m_uid) { if ((cp=asccasestr(responded_other_text, "UID "))) { u = atol(&cp[4]); for (n = bot; n <= top; n++) if (m[n-1].m_uid == u) break; if (n > top) { imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0); continue; } } else n = -1; } else { n = responded_other_number; if (n <= 0 || n > msgCount) { imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0); continue; } } imap_fetchdata(mp, &mt, expected, NEED_HEADER, NULL, 0, 0); if (n >= 0 && !(m[n-1].m_have & HAVE_HEADER)) commitmsg(mp, &m[n-1], mt, HAVE_HEADER); if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) { if (verbose) fputs(imapbuf, stderr); if ((cp = asccasestr(imapbuf, "UID ")) != NULL) { u = atol(&cp[4]); for (n = bot; n <= top; n++) if (m[n-1].m_uid == u) break; if (n <= top && !(m[n-1].m_have & HAVE_HEADER)) commitmsg(mp, &m[n-1], mt, HAVE_HEADER); n = 0; } } } while (mp->mb_active & MB_COMD) ok = imap_answer(mp, 1); return ok; } void imap_getheaders(int bot, int top) { sighandler_type saveint, savepipe; enum okay ok = STOP; int i, chunk = 256; (void)&saveint; (void)&savepipe; (void)&ok; (void)⊥ (void)⊤ verbose = value("verbose") != NULL; if (mb.mb_type == MB_CACHE) return; if (bot < 1) bot = 1; if (top > msgCount) top = msgCount; for (i = bot; i < top; i++) { if (message[i-1].m_have & HAVE_HEADER || getcache(&mb, &message[i-1], NEED_HEADER) == OKAY) bot = i+1; else break; } for (i = top; i > bot; i--) { if (message[i-1].m_have & HAVE_HEADER || getcache(&mb, &message[i-1], NEED_HEADER) == OKAY) top = i-1; else break; } if (bot >= top) return; imaplock = 1; if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN) safe_signal(SIGINT, maincatch); savepipe = safe_signal(SIGPIPE, SIG_IGN); if (sigsetjmp(imapjmp, 1) == 0) { if (savepipe != SIG_IGN) safe_signal(SIGPIPE, imapcatch); for (i = bot; i <= top; i += chunk) { ok = imap_fetchheaders(&mb, message, i, i+chunk-1 < top ? i+chunk-1 : top); if (interrupts) onintr(0); } } safe_signal(SIGINT, saveint); safe_signal(SIGPIPE, savepipe); imaplock = 0; } static enum okay imap_exit(struct mailbox *mp) { char o[LINESIZE]; FILE *queuefp = NULL; verbose = value("verbose") != NULL; mp->mb_active |= MB_BYE; snprintf(o, sizeof o, "%s LOGOUT\r\n", tag(1)); IMAP_OUT(o, MB_COMD, return STOP) IMAP_ANSWER() return OKAY; } static enum okay imap_delete(struct mailbox *mp, int n, struct message *m, int needstat) { imap_store(mp, m, n, '+', "\\Deleted", needstat); if (mp->mb_type == MB_IMAP) delcache(mp, m); return OKAY; } static enum okay imap_close(struct mailbox *mp) { char o[LINESIZE]; FILE *queuefp = NULL; snprintf(o, sizeof o, "%s CLOSE\r\n", tag(1)); IMAP_OUT(o, MB_COMD, return STOP) IMAP_ANSWER() return OKAY; } static enum okay imap_update(struct mailbox *mp) { FILE *readstat = NULL; struct message *m; int dodel, c, gotcha = 0, held = 0, modflags = 0, needstat, stored = 0; verbose = value("verbose") != NULL; if (Tflag != NULL) { if ((readstat = Zopen(Tflag, "w", NULL)) == NULL) Tflag = NULL; } if (!edit && mp->mb_perm != 0) { holdbits(); for (m = &message[0], c = 0; m < &message[msgCount]; m++) { if (m->m_flag & MBOX) c++; } if (c > 0) if (makembox() == STOP) goto bypass; } for (m = &message[0], gotcha=0, held=0; m < &message[msgCount]; m++) { if (readstat != NULL && (m->m_flag & (MREAD|MDELETED)) != 0) { char *id; if ((id = hfield("message-id", m)) != NULL || (id = hfield("article-id", m)) != NULL) fprintf(readstat, "%s\n", id); } if (mp->mb_perm == 0) { dodel = 0; } else if (edit) { dodel = m->m_flag & MDELETED; } else { dodel = !((m->m_flag&MPRESERVE) || (m->m_flag&MTOUCH) == 0); } /* * Fetch the result after around each 800 STORE commands * sent (approx. 32k data sent). Otherwise, servers will * try to flush the return queue at some point, leading * to a deadlock if we are still writing commands but not * reading their results. */ needstat = stored > 0 && stored % 800 == 0; /* * Even if this message has been deleted, continue * to set further flags. This is necessary to support * Gmail semantics, where "delete" actually means * "archive", and the flags are applied to the copy * in "All Mail". */ if ((m->m_flag&(MREAD|MSTATUS)) == (MREAD|MSTATUS)) { imap_store(mp, m, m-message+1, '+', "\\Seen", needstat); stored++; } if (m->m_flag & MFLAG) { imap_store(mp, m, m-message+1, '+', "\\Flagged", needstat); stored++; } if (m->m_flag & MUNFLAG) { imap_store(mp, m, m-message+1, '-', "\\Flagged", needstat); stored++; } if (m->m_flag & MANSWER) { imap_store(mp, m, m-message+1, '+', "\\Answered", needstat); stored++; } if (m->m_flag & MUNANSWER) { imap_store(mp, m, m-message+1, '-', "\\Answered", needstat); stored++; } if (m->m_flag & MDRAFT) { imap_store(mp, m, m-message+1, '+', "\\Draft", needstat); stored++; } if (m->m_flag & MUNDRAFT) { imap_store(mp, m, m-message+1, '-', "\\Draft", needstat); stored++; } if (dodel) { imap_delete(mp, m-message+1, m, needstat); stored++; gotcha++; } else if (mp->mb_type != MB_CACHE || !edit && (!(m->m_flag&(MBOXED|MSAVED|MDELETED)) || (m->m_flag & (MBOXED|MPRESERVE|MTOUCH)) == (MPRESERVE|MTOUCH)) || edit && !(m->m_flag & MDELETED)) held++; if (m->m_flag & MNEW) { m->m_flag &= ~MNEW; m->m_flag |= MSTATUS; } } bypass: if (readstat != NULL) Fclose(readstat); if (gotcha) imap_close(mp); for (m = &message[0]; m < &message[msgCount]; m++) if (!(m->m_flag&MUNLINKED) && m->m_flag&(MBOXED|MDELETED|MSAVED|MSTATUS| MFLAG|MUNFLAG|MANSWER|MUNANSWER| MDRAFT|MUNDRAFT)) { putcache(mp, m); modflags++; } if ((gotcha || modflags) && edit) { printf(catgets(catd, CATSET, 168, "\"%s\" "), mailname); printf(value("bsdcompat") || value("bsdmsgs") ? catgets(catd, CATSET, 170, "complete\n") : catgets(catd, CATSET, 212, "updated.\n")); } else if (held && !edit && mp->mb_perm != 0) { if (held == 1) printf(catgets(catd, CATSET, 155, "Held 1 message in %s\n"), mailname); else if (held > 1) printf(catgets(catd, CATSET, 156, "Held %d messages in %s\n"), held, mailname); } fflush(stdout); return OKAY; } void imap_quit(void) { sighandler_type saveint; sighandler_type savepipe; verbose = value("verbose") != NULL; if (mb.mb_type == MB_CACHE) { imap_update(&mb); return; } if (mb.mb_sock.s_fd < 0) { fprintf(stderr, "IMAP connection closed.\n"); return; } imaplock = 1; saveint = safe_signal(SIGINT, SIG_IGN); savepipe = safe_signal(SIGPIPE, SIG_IGN); if (sigsetjmp(imapjmp, 1)) { safe_signal(SIGINT, saveint); safe_signal(SIGPIPE, saveint); imaplock = 0; return; } if (saveint != SIG_IGN) safe_signal(SIGINT, imapcatch); if (savepipe != SIG_IGN) safe_signal(SIGPIPE, imapcatch); imap_update(&mb); if (!same_imap_account) { imap_exit(&mb); sclose(&mb.mb_sock); } safe_signal(SIGINT, saveint); safe_signal(SIGPIPE, savepipe); imaplock = 0; } static enum okay imap_store(struct mailbox *mp, struct message *m, int n, int c, const char *sp, int needstat) { char o[LINESIZE]; FILE *queuefp = NULL; if (mp->mb_type == MB_CACHE && (queuefp = cache_queue(mp)) == NULL) return STOP; if (m->m_uid) snprintf(o, sizeof o, "%s UID STORE %lu %cFLAGS (%s)\r\n", tag(1), m->m_uid, c, sp); else { if (check_expunged() == STOP) return STOP; snprintf(o, sizeof o, "%s STORE %u %cFLAGS (%s)\r\n", tag(1), n, c, sp); } IMAP_OUT(o, MB_COMD, return STOP) if (needstat) IMAP_ANSWER() else mb.mb_active &= ~MB_COMD; if (queuefp != NULL) Fclose(queuefp); return OKAY; } enum okay imap_undelete(struct message *m, int n) { return imap_unstore(m, n, "\\Deleted"); } enum okay imap_unread(struct message *m, int n) { return imap_unstore(m, n, "\\Seen"); } static enum okay imap_unstore(struct message *m, int n, const char *flag) { sighandler_type saveint, savepipe; enum okay ok = STOP; (void)&saveint; (void)&savepipe; (void)&ok; verbose = value("verbose") != NULL; imaplock = 1; if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN) safe_signal(SIGINT, maincatch); savepipe = safe_signal(SIGPIPE, SIG_IGN); if (sigsetjmp(imapjmp, 1) == 0) { if (savepipe != SIG_IGN) safe_signal(SIGPIPE, imapcatch); ok = imap_store(&mb, m, n, '-', flag, 1); } safe_signal(SIGINT, saveint); safe_signal(SIGPIPE, savepipe); imaplock = 0; if (interrupts) onintr(0); return ok; } static const char * tag(int new) { static char ts[20]; static long n; if (new) n++; snprintf(ts, sizeof ts, "T%lu", n); return ts; } int imap_imap(void *vp) { sighandler_type saveint, savepipe; char o[LINESIZE]; enum okay ok = STOP; struct mailbox *mp = &mb; FILE *queuefp = NULL; (void)&saveint; (void)&savepipe; (void)&ok; verbose = value("verbose") != NULL; if (mp->mb_type != MB_IMAP) { printf("Not operating on an IMAP mailbox.\n"); return 1; } imaplock = 1; if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN) safe_signal(SIGINT, maincatch); savepipe = safe_signal(SIGPIPE, SIG_IGN); if (sigsetjmp(imapjmp, 1) == 0) { if (savepipe != SIG_IGN) safe_signal(SIGPIPE, imapcatch); snprintf(o, sizeof o, "%s %s\r\n", tag(1), (char *)vp); IMAP_OUT(o, MB_COMD, goto out) while (mp->mb_active & MB_COMD) { ok = imap_answer(mp, 0); fputs(responded_text, stdout); } } out: safe_signal(SIGINT, saveint); safe_signal(SIGPIPE, savepipe); imaplock = 0; if (interrupts) onintr(0); return ok != OKAY; } int imap_newmail(int autoinc) { if (autoinc && had_exists < 0 && had_expunge < 0) { verbose = value("verbose") != NULL; imaplock = 1; imap_noop(); imaplock = 0; } if (had_exists == msgCount && had_expunge < 0) /* * Some servers always respond with EXISTS to NOOP. If * the mailbox has been changed but the number of messages * has not, an EXPUNGE must also had been sent; otherwise, * nothing has changed. */ had_exists = -1; return had_expunge >= 0 ? 2 : had_exists >= 0 ? 1 : 0; } static char * imap_putflags(int f) { const char *cp; char *buf, *bp; bp = buf = salloc(100); if (f & (MREAD|MFLAGGED|MANSWERED|MDRAFTED)) { *bp++ = '('; if (f & MREAD) { if (bp[-1] != '(') *bp++ = ' '; for (cp = "\\Seen"; *cp; cp++) *bp++ = *cp; } if (f & MFLAGGED) { if (bp[-1] != '(') *bp++ = ' '; for (cp = "\\Flagged"; *cp; cp++) *bp++ = *cp; } if (f & MANSWERED) { if (bp[-1] != '(') *bp++ = ' '; for (cp = "\\Answered"; *cp; cp++) *bp++ = *cp; } if (f & MDRAFT) { if (bp[-1] != '(') *bp++ = ' '; for (cp = "\\Draft"; *cp; cp++) *bp++ = *cp; } *bp++ = ')'; *bp++ = ' '; } *bp = '\0'; return buf; } static void imap_getflags(const char *cp, char **xp, enum mflag *f) { while (*cp != ')') { if (*cp == '\\') { if (ascncasecmp(cp, "\\Seen", 5) == 0) *f |= MREAD; else if (ascncasecmp(cp, "\\Recent", 7) == 0) *f |= MNEW; else if (ascncasecmp(cp, "\\Deleted", 8) == 0) *f |= MDELETED; else if (ascncasecmp(cp, "\\Flagged", 8) == 0) *f |= MFLAGGED; else if (ascncasecmp(cp, "\\Answered", 9) == 0) *f |= MANSWERED; else if (ascncasecmp(cp, "\\Draft", 6) == 0) *f |= MDRAFTED; } cp++; } if (xp) *xp = (char *)cp; } static enum okay imap_append1(struct mailbox *mp, const char *name, FILE *fp, off_t off1, long xsize, enum mflag flag, time_t t) { char o[LINESIZE]; char *buf; size_t bufsize, buflen, count; enum okay ok = STOP; long size, lines, ysize; int twice = 0; FILE *queuefp = NULL; if (mp->mb_type == MB_CACHE) { queuefp = cache_queue(mp); if (queuefp == NULL) return STOP; ok = OKAY; } buf = smalloc(bufsize = LINESIZE); buflen = 0; again: size = xsize; count = fsize(fp); fseek(fp, off1, SEEK_SET); snprintf(o, sizeof o, "%s APPEND %s %s%s {%ld}\r\n", tag(1), imap_quotestr(name), imap_putflags(flag), imap_make_date_time(t), size); IMAP_OUT(o, MB_COMD, goto out) while (mp->mb_active & MB_COMD) { ok = imap_answer(mp, twice); if (response_type == RESPONSE_CONT) break; } if (mp->mb_type != MB_CACHE && ok == STOP) { if (twice == 0) goto trycreate; else goto out; } lines = ysize = 0; while (size > 0) { fgetline(&buf, &bufsize, &count, &buflen, fp, 1); lines++; ysize += buflen; buf[buflen-1] = '\r'; buf[buflen] = '\n'; if (mp->mb_type != MB_CACHE) swrite1(&mp->mb_sock, buf, buflen+1, 1); else if (queuefp) fwrite(buf, 1, buflen+1, queuefp); size -= buflen+1; } if (mp->mb_type != MB_CACHE) swrite(&mp->mb_sock, "\r\n"); else if (queuefp) fputs("\r\n", queuefp); while (mp->mb_active & MB_COMD) { ok = imap_answer(mp, 0); if (response_status == RESPONSE_NO /*&& ascncasecmp(responded_text, "[TRYCREATE] ", 12) == 0*/) { trycreate: if (twice++) { ok = STOP; goto out; } snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), imap_quotestr(name)); IMAP_OUT(o, MB_COMD, goto out); while (mp->mb_active & MB_COMD) ok = imap_answer(mp, 1); if (ok == STOP) goto out; imap_created_mailbox++; goto again; } else if (ok != OKAY) fprintf(stderr, "IMAP error: %s", responded_text); else if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) imap_appenduid(mp, fp, t, off1, xsize, ysize, lines, flag, name); } out: if (queuefp != NULL) Fclose(queuefp); free(buf); return ok; } static enum okay imap_append0(struct mailbox *mp, const char *name, FILE *fp) { char *buf, *bp, *lp; size_t bufsize, buflen, count; off_t off1 = -1, offs; int inhead = 1; int flag = MNEW|MNEWEST; long size = 0; time_t tim; enum okay ok; buf = smalloc(bufsize = LINESIZE); buflen = 0; count = fsize(fp); offs = ftell(fp); time(&tim); do { bp = fgetline(&buf, &bufsize, &count, &buflen, fp, 1); if (bp == NULL || strncmp(buf, "From ", 5) == 0) { if (off1 != (off_t)-1) { ok=imap_append1(mp, name, fp, off1, size, flag, tim); if (ok == STOP) return STOP; fseek(fp, offs+buflen, SEEK_SET); } off1 = offs + buflen; size = 0; inhead = 1; flag = MNEW; if (bp != NULL) tim = unixtime(buf); } else size += buflen+1; offs += buflen; if (bp && buf[0] == '\n') inhead = 0; else if (bp && inhead && ascncasecmp(buf, "status", 6) == 0) { lp = &buf[6]; while (whitechar(*lp&0377)) lp++; if (*lp == ':') while (*++lp != '\0') switch (*lp) { case 'R': flag |= MREAD; break; case 'O': flag &= ~MNEW; break; } } else if (bp && inhead && ascncasecmp(buf, "x-status", 8) == 0) { lp = &buf[8]; while (whitechar(*lp&0377)) lp++; if (*lp == ':') while (*++lp != '\0') switch (*lp) { case 'F': flag |= MFLAGGED; break; case 'A': flag |= MANSWERED; break; case 'T': flag |= MDRAFTED; break; } } } while (bp != NULL); free(buf); return OKAY; } enum okay imap_append(const char *xserver, FILE *fp) { sighandler_type saveint, savepipe; char *server, *uhp, *mbx, *user; const char *sp, *cp, *pass; int use_ssl; enum okay ok = STOP; (void)&saveint; (void)&savepipe; (void)&ok; verbose = value("verbose") != NULL; server = savestr((char *)xserver); imap_split(&server, &sp, &use_ssl, &cp, &uhp, &mbx, &pass, &user); imaplock = 1; if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN) safe_signal(SIGINT, maincatch); savepipe = safe_signal(SIGPIPE, SIG_IGN); if (sigsetjmp(imapjmp, 1)) goto out; if (savepipe != SIG_IGN) safe_signal(SIGPIPE, imapcatch); if ((mb.mb_type == MB_CACHE || mb.mb_sock.s_fd > 0) && mb.mb_imap_account && strcmp(protbase(server), mb.mb_imap_account) == 0) { ok = imap_append0(&mb, mbx, fp); } else { struct mailbox mx; memset(&mx, 0, sizeof mx); if (disconnected(server) == 0) { if (sopen(sp, &mx.mb_sock, use_ssl, uhp, use_ssl ? "imaps" : "imap", verbose) != OKAY) goto fail; mx.mb_sock.s_desc = "IMAP"; mx.mb_type = MB_IMAP; mx.mb_imap_account = (char *)protbase(server); mx.mb_imap_mailbox = mbx; if (imap_preauth(&mx, sp, uhp) != OKAY || imap_auth(&mx, uhp, user, pass)!=OKAY) { sclose(&mx.mb_sock); goto fail; } ok = imap_append0(&mx, mbx, fp); imap_exit(&mx); sclose(&mx.mb_sock); } else { mx.mb_imap_account = (char *)protbase(server); mx.mb_imap_mailbox = mbx; mx.mb_type = MB_CACHE; ok = imap_append0(&mx, mbx, fp); } fail:; } out: safe_signal(SIGINT, saveint); safe_signal(SIGPIPE, savepipe); imaplock = 0; if (interrupts) onintr(0); return ok; } static enum okay imap_list1(struct mailbox *mp, const char *base, struct list_item **list, struct list_item **lend, int level) { char o[LINESIZE]; enum okay ok = STOP; char *cp; const char *bp; FILE *queuefp = NULL; struct list_item *lp; *list = *lend = NULL; snprintf(o, sizeof o, "%s LIST %s %%\r\n", tag(1), imap_quotestr(base)); IMAP_OUT(o, MB_COMD, return STOP); while (mp->mb_active & MB_COMD) { ok = imap_answer(mp, 1); if (response_status == RESPONSE_OTHER && response_other == MAILBOX_DATA_LIST && imap_parse_list() == OKAY) { cp = imap_unquotestr(list_name); lp = csalloc(1, sizeof *lp); lp->l_name = cp; for (bp = base; *bp && *bp == *cp; bp++) cp++; lp->l_base = *cp ? cp : savestr(base); lp->l_attr = list_attributes; lp->l_level = level+1; lp->l_delim = list_hierarchy_delimiter; if (*list && *lend) { (*lend)->l_next = lp; *lend = lp; } else *list = *lend = lp; } } return ok; } static enum okay imap_list(struct mailbox *mp, const char *base, int strip, FILE *fp) { struct list_item *list, *lend, *lp, *lx, *ly; int n; const char *bp; char *cp; int depth; verbose = value("verbose") != NULL; depth = (cp = value("imap-list-depth")) != NULL ? atoi(cp) : 2; if (imap_list1(mp, base, &list, &lend, 0) == STOP) return STOP; if (list == NULL || lend == NULL) return OKAY; for (lp = list; lp; lp = lp->l_next) if (lp->l_delim != '/' && lp->l_delim != EOF && lp->l_level < depth && (lp->l_attr&LIST_NOINFERIORS) == 0) { cp = salloc((n = strlen(lp->l_name)) + 2); strcpy(cp, lp->l_name); cp[n] = lp->l_delim; cp[n+1] = '\0'; if (imap_list1(mp, cp, &lx, &ly, lp->l_level) == OKAY && lx && ly) { lp->l_has_children = 1; if (strcmp(cp, lx->l_name) == 0) lx = lx->l_next; if (lx) { lend->l_next = lx; lend = ly; } } } for (lp = list; lp; lp = lp->l_next) { if (strip) { cp = lp->l_name; for (bp = base; *bp && *bp == *cp; bp++) cp++; } else cp = lp->l_name; if ((lp->l_attr&LIST_NOSELECT) == 0) fprintf(fp, "%s\n", *cp ? cp : base); else if (lp->l_has_children == 0) fprintf(fp, "%s%c\n", *cp ? cp : base, lp->l_delim != EOF ? lp->l_delim : '\n'); } return OKAY; } void imap_folders(const char *name, int strip) { sighandler_type saveint, savepipe; const char *fold, *cp, *sp; char *tempfn; FILE *fp; int columnize = is_a_tty[1]; (void)&saveint; (void)&savepipe; (void)&fp; (void)&fold; cp = protbase(name); sp = mb.mb_imap_account; if (strcmp(cp, sp)) { fprintf(stderr, "Cannot list folders on other than the " "current IMAP account,\n\"%s\". " "Try \"folders @\".\n", sp); return; } fold = protfile(name); if (columnize) { if ((fp = Ftemp(&tempfn, "Ri", "w+", 0600, 1)) == NULL) { perror("tmpfile"); return; } rm(tempfn); Ftfree(&tempfn); } else fp = stdout; imaplock = 1; if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN) safe_signal(SIGINT, maincatch); savepipe = safe_signal(SIGPIPE, SIG_IGN); if (sigsetjmp(imapjmp, 1)) goto out; if (savepipe != SIG_IGN) safe_signal(SIGPIPE, imapcatch); if (mb.mb_type == MB_CACHE) cache_list(&mb, fold, strip, fp); else imap_list(&mb, fold, strip, fp); imaplock = 0; if (interrupts) { if (columnize) Fclose(fp); onintr(0); } fflush(fp); if (columnize) { rewind(fp); if (fsize(fp) > 0) dopr(fp); else fprintf(stderr, "Folder not found.\n"); } out: safe_signal(SIGINT, saveint); safe_signal(SIGPIPE, savepipe); if (columnize) Fclose(fp); } static void dopr(FILE *fp) { char o[LINESIZE], *tempfn; int c; long n = 0, mx = 0, columns, width; FILE *out; if ((out = Ftemp(&tempfn, "Ro", "w+", 0600, 1)) == NULL) { perror("tmpfile"); return; } rm(tempfn); Ftfree(&tempfn); while ((c = getc(fp)) != EOF) { if (c == '\n') { if (n > mx) mx = n; n = 0; } else n++; } rewind(fp); width = scrnwidth; if (mx < width / 2) { columns = width / (mx+2); snprintf(o, sizeof o, "sort | pr -%lu -w%lu -t", columns, width); } else strncpy(o, "sort", sizeof o)[sizeof o - 1] = '\0'; run_command(SHELL, 0, fileno(fp), fileno(out), "-c", o, NULL); try_pager(out); Fclose(out); } static enum okay imap_copy1(struct mailbox *mp, struct message *m, int n, const char *name) { char o[LINESIZE]; const char *qname; enum okay ok = STOP; int twice = 0; int stored = 0; FILE *queuefp = NULL; if (mp->mb_type == MB_CACHE) { if ((queuefp = cache_queue(mp)) == NULL) return STOP; ok = OKAY; } qname = imap_quotestr(name = protfile(name)); /* * Since it is not possible to set flags on the copy, recently * set flags must be set on the original to include it in the copy. */ if ((m->m_flag&(MREAD|MSTATUS)) == (MREAD|MSTATUS)) imap_store(mp, m, n, '+', "\\Seen", 0); if (m->m_flag&MFLAG) imap_store(mp, m, n, '+', "\\Flagged", 0); if (m->m_flag&MUNFLAG) imap_store(mp, m, n, '-', "\\Flagged", 0); if (m->m_flag&MANSWER) imap_store(mp, m, n, '+', "\\Answered", 0); if (m->m_flag&MUNANSWER) imap_store(mp, m, n, '-', "\\Flagged", 0); if (m->m_flag&MDRAFT) imap_store(mp, m, n, '+', "\\Draft", 0); if (m->m_flag&MUNDRAFT) imap_store(mp, m, n, '-', "\\Draft", 0); again: if (m->m_uid) snprintf(o, sizeof o, "%s UID COPY %lu %s\r\n", tag(1), m->m_uid, qname); else { if (check_expunged() == STOP) goto out; snprintf(o, sizeof o, "%s COPY %u %s\r\n", tag(1), n, qname); } IMAP_OUT(o, MB_COMD, goto out) while (mp->mb_active & MB_COMD) ok = imap_answer(mp, twice); if (mp->mb_type == MB_IMAP && mp->mb_flags & MB_UIDPLUS && response_status == RESPONSE_OK) imap_copyuid(mp, m, name); if (response_status == RESPONSE_NO && twice++ == 0) { snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname); IMAP_OUT(o, MB_COMD, goto out) while (mp->mb_active & MB_COMD) ok = imap_answer(mp, 1); if (ok == OKAY) { imap_created_mailbox++; goto again; } } if (queuefp != NULL) Fclose(queuefp); /* * ... and reset the flag to its initial value so that * the 'exit' command still leaves the message unread. */ out: if ((m->m_flag&(MREAD|MSTATUS)) == (MREAD|MSTATUS)) { imap_store(mp, m, n, '-', "\\Seen", 0); stored++; } if (m->m_flag&MFLAG) { imap_store(mp, m, n, '-', "\\Flagged", 0); stored++; } if (m->m_flag&MUNFLAG) { imap_store(mp, m, n, '+', "\\Flagged", 0); stored++; } if (m->m_flag&MANSWER) { imap_store(mp, m, n, '-', "\\Answered", 0); stored++; } if (m->m_flag&MUNANSWER) { imap_store(mp, m, n, '+', "\\Answered", 0); stored++; } if (m->m_flag&MDRAFT) { imap_store(mp, m, n, '-', "\\Draft", 0); stored++; } if (m->m_flag&MUNDRAFT) { imap_store(mp, m, n, '+', "\\Draft", 0); stored++; } if (stored) { mp->mb_active |= MB_COMD; imap_finish(mp); } return ok; } enum okay imap_copy(struct message *m, int n, const char *name) { sighandler_type saveint, savepipe; enum okay ok = STOP; (void)&saveint; (void)&savepipe; (void)&ok; verbose = value("verbose") != NULL; imaplock = 1; if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN) safe_signal(SIGINT, maincatch); savepipe = safe_signal(SIGPIPE, SIG_IGN); if (sigsetjmp(imapjmp, 1) == 0) { if (savepipe != SIG_IGN) safe_signal(SIGPIPE, imapcatch); ok = imap_copy1(&mb, m, n, name); } safe_signal(SIGINT, saveint); safe_signal(SIGPIPE, savepipe); imaplock = 0; if (interrupts) onintr(0); return ok; } static enum okay imap_copyuid_parse(const char *cp, unsigned long *uidvalidity, unsigned long *olduid, unsigned long *newuid) { char *xp, *yp, *zp; *uidvalidity = strtoul(cp, &xp, 10); *olduid = strtoul(xp, &yp, 10); *newuid = strtoul(yp, &zp, 10); return *uidvalidity && *olduid && *newuid && xp > cp && *xp == ' ' && yp > xp && *yp == ' ' && zp > yp && *zp == ']'; } static enum okay imap_appenduid_parse(const char *cp, unsigned long *uidvalidity, unsigned long *uid) { char *xp, *yp; *uidvalidity = strtoul(cp, &xp, 10); *uid = strtoul(xp, &yp, 10); return *uidvalidity && *uid && xp > cp && *xp == ' ' && yp > xp && *yp == ']'; } static enum okay imap_copyuid(struct mailbox *mp, struct message *m, const char *name) { const char *cp; unsigned long uidvalidity, olduid, newuid; struct mailbox xmb; struct message xm; if ((cp = asccasestr(responded_text, "[COPYUID ")) == NULL || imap_copyuid_parse(&cp[9], &uidvalidity, &olduid, &newuid) == STOP) return STOP; xmb = *mp; xmb.mb_cache_directory = NULL; xmb.mb_imap_mailbox = savestr((char *)name); xmb.mb_uidvalidity = uidvalidity; initcache(&xmb); if (m == NULL) { memset(&xm, 0, sizeof xm); xm.m_uid = olduid; if (getcache1(mp, &xm, NEED_UNSPEC, 3) != OKAY) return STOP; getcache(mp, &xm, NEED_HEADER); getcache(mp, &xm, NEED_BODY); } else { if ((m->m_flag & HAVE_HEADER) == 0) getcache(mp, m, NEED_HEADER); if ((m->m_flag & HAVE_BODY) == 0) getcache(mp, m, NEED_BODY); xm = *m; } xm.m_uid = newuid; xm.m_flag &= ~MFULLYCACHED; putcache(&xmb, &xm); return OKAY; } static enum okay imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1, long xsize, long size, long lines, int flag, const char *name) { const char *cp; unsigned long uidvalidity, uid; struct mailbox xmb; struct message xm; if ((cp = asccasestr(responded_text, "[APPENDUID ")) == NULL || imap_appenduid_parse(&cp[11], &uidvalidity, &uid) == STOP) return STOP; xmb = *mp; xmb.mb_cache_directory = NULL; xmb.mb_imap_mailbox = savestr((char *)name); xmb.mb_uidvalidity = uidvalidity; xmb.mb_otf = xmb.mb_itf = fp; initcache(&xmb); memset(&xm, 0, sizeof xm); xm.m_flag = flag&MREAD | MNEW; xm.m_time = t; xm.m_block = mailx_blockof(off1); xm.m_offset = mailx_offsetof(off1); xm.m_size = size; xm.m_xsize = xsize; xm.m_lines = xm.m_xlines = lines; xm.m_uid = uid; xm.m_have = HAVE_HEADER|HAVE_BODY; putcache(&xmb, &xm); return OKAY; } static enum okay imap_appenduid_cached(struct mailbox *mp, FILE *fp) { FILE *tp = NULL; time_t t; long size, xsize, ysize, lines; enum mflag flag = MNEW; char *name, *buf, *bp, *cp, *tempCopy; size_t bufsize, buflen, count; enum okay ok = STOP; buf = smalloc(bufsize = LINESIZE); buflen = 0; count = fsize(fp); if (fgetline(&buf, &bufsize, &count, &buflen, fp, 0) == NULL) goto stop; for (bp = buf; *bp != ' '; bp++); /* strip old tag */ while (*bp == ' ') bp++; if ((cp = strrchr(bp, '{')) == NULL) goto stop; xsize = atol(&cp[1]) + 2; if ((name = imap_strex(&bp[7], &cp)) == NULL) goto stop; while (*cp == ' ') cp++; if (*cp == '(') { imap_getflags(cp, &cp, &flag); while (*++cp == ' '); } t = imap_read_date_time(cp); if ((tp = Ftemp(&tempCopy, "Rc", "w+", 0600, 1)) == NULL) goto stop; rm(tempCopy); Ftfree(&tempCopy); size = xsize; ysize = lines = 0; while (size > 0) { if (fgetline(&buf, &bufsize, &count, &buflen, fp, 0) == NULL) goto stop; size -= buflen; buf[--buflen] = '\0'; buf[buflen-1] = '\n'; fwrite(buf, 1, buflen, tp); ysize += buflen; lines++; } fflush(tp); rewind(tp); imap_appenduid(mp, tp, t, 0, xsize-2, ysize-1, lines-1, flag, imap_unquotestr(name)); ok = OKAY; stop: free(buf); if (tp) Fclose(tp); return ok; } static enum okay imap_search2(struct mailbox *mp, struct message *m, int count, const char *spec, int f) { char *o; size_t osize; FILE *queuefp = NULL; enum okay ok = STOP; int i; unsigned long n; const char *cp; char *xp, *cs, c; c = 0; for (cp = spec; *cp; cp++) c |= *cp; if (c & 0200) { cp = gettcharset(); #ifdef HAVE_ICONV if (asccasecmp(cp, "utf-8")) { iconv_t it; char *sp, *nsp, *nspec; size_t sz, nsz; if ((it = iconv_open_ft("utf-8", cp)) != (iconv_t)-1) { sz = strlen(spec) + 1; nsp = nspec = salloc(nsz = 6*strlen(spec) + 1); sp = (char *)spec; if (iconv(it, &sp, &sz, &nsp, &nsz) != (size_t)-1 && sz == 0) { spec = nspec; cp = "utf-8"; } iconv_close(it); } } #endif /* HAVE_ICONV */ cp = imap_quotestr(cp); cs = salloc(n = strlen(cp) + 10); snprintf(cs, n, "CHARSET %s ", cp); } else cs = ""; o = ac_alloc(osize = strlen(spec) + 60); snprintf(o, osize, "%s UID SEARCH %s%s\r\n", tag(1), cs, spec); IMAP_OUT(o, MB_COMD, goto out) while (mp->mb_active & MB_COMD) { ok = imap_answer(mp, 0); if (response_status == RESPONSE_OTHER && response_other == MAILBOX_DATA_SEARCH) { xp = responded_other_text; while (*xp && *xp != '\r') { n = strtoul(xp, &xp, 10); for (i = 0; i < count; i++) if (m[i].m_uid == n && (m[i].m_flag&MHIDDEN) == 0 && (f == MDELETED || (m[i].m_flag&MDELETED) == 0)) mark(i+1, f); } } } out: ac_free(o); return ok; } enum okay imap_search1(const char *spec, int f) { sighandler_type saveint, savepipe; enum okay ok = STOP; (void)&saveint; (void)&savepipe; (void)&ok; if (mb.mb_type != MB_IMAP) return STOP; verbose = value("verbose") != NULL; imaplock = 1; if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN) safe_signal(SIGINT, maincatch); savepipe = safe_signal(SIGPIPE, SIG_IGN); if (sigsetjmp(imapjmp, 1) == 0) { if (savepipe != SIG_IGN) safe_signal(SIGPIPE, imapcatch); ok = imap_search2(&mb, message, msgCount, spec, f); } safe_signal(SIGINT, saveint); safe_signal(SIGPIPE, savepipe); imaplock = 0; if (interrupts) onintr(0); return ok; } int imap_thisaccount(const char *cp) { if (mb.mb_type != MB_CACHE && mb.mb_type != MB_IMAP) return 0; if ((mb.mb_type != MB_CACHE && mb.mb_sock.s_fd < 0) || mb.mb_imap_account == NULL) return 0; return strcmp(protbase(cp), mb.mb_imap_account) == 0; } enum okay imap_remove(const char *name) { sighandler_type saveint, savepipe; enum okay ok = STOP; (void)&saveint; (void)&savepipe; (void)&ok; verbose = value("verbose") != NULL; if (mb.mb_type != MB_IMAP) { fprintf(stderr, "Refusing to remove \"%s\" " "in disconnected mode.\n", name); return STOP; } if (!imap_thisaccount(name)) { fprintf(stderr, "Can only remove mailboxes on current IMAP " "server: \"%s\" not removed.\n", name); return STOP; } imaplock = 1; if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN) safe_signal(SIGINT, maincatch); savepipe = safe_signal(SIGPIPE, SIG_IGN); if (sigsetjmp(imapjmp, 1) == 0) { if (savepipe != SIG_IGN) safe_signal(SIGPIPE, imapcatch); ok = imap_remove1(&mb, protfile(name)); } safe_signal(SIGINT, saveint); safe_signal(SIGPIPE, savepipe); imaplock = 0; if (ok == OKAY) ok = cache_remove(name); if (interrupts) onintr(0); return ok; } static enum okay imap_remove1(struct mailbox *mp, const char *name) { FILE *queuefp = NULL; char *o; int os; enum okay ok = STOP; o = ac_alloc(os = 2*strlen(name) + 100); snprintf(o, os, "%s DELETE %s\r\n", tag(1), imap_quotestr(name)); IMAP_OUT(o, MB_COMD, goto out) while (mp->mb_active & MB_COMD) ok = imap_answer(mp, 1); out: ac_free(o); return ok; } enum okay imap_rename(const char *old, const char *new) { sighandler_type saveint, savepipe; enum okay ok = STOP; (void)&saveint; (void)&savepipe; (void)&ok; verbose = value("verbose") != NULL; if (mb.mb_type != MB_IMAP) { fprintf(stderr, "Refusing to rename mailboxes " "in disconnected mode.\n"); return STOP; } if (!imap_thisaccount(old) || !imap_thisaccount(new)) { fprintf(stderr, "Can only rename mailboxes on current IMAP " "server: \"%s\" not renamed to \"%s\".\n", old, new); return STOP; } imaplock = 1; if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN) safe_signal(SIGINT, maincatch); savepipe = safe_signal(SIGPIPE, SIG_IGN); if (sigsetjmp(imapjmp, 1) == 0) { if (savepipe != SIG_IGN) safe_signal(SIGPIPE, imapcatch); ok = imap_rename1(&mb, protfile(old), protfile(new)); } safe_signal(SIGINT, saveint); safe_signal(SIGPIPE, savepipe); imaplock = 0; if (ok == OKAY) ok = cache_rename(old, new); if (interrupts) onintr(0); return ok; } static enum okay imap_rename1(struct mailbox *mp, const char *old, const char *new) { FILE *queuefp = NULL; char *o; int os; enum okay ok = STOP; o = ac_alloc(os = 2*strlen(old) + 2*strlen(new) + 100); snprintf(o, os, "%s RENAME %s %s\r\n", tag(1), imap_quotestr(old), imap_quotestr(new)); IMAP_OUT(o, MB_COMD, goto out) while (mp->mb_active & MB_COMD) ok = imap_answer(mp, 1); out: ac_free(o); return ok; } enum okay imap_dequeue(struct mailbox *mp, FILE *fp) { FILE *queuefp = NULL; char o[LINESIZE], *newname; char *buf, *bp, *cp, iob[4096]; size_t bufsize, buflen, count; enum okay ok = OKAY, rok = OKAY; long offs, offs1, offs2, octets; int n, twice, gotcha = 0; buf = smalloc(bufsize = LINESIZE); buflen = 0; count = fsize(fp); while (offs1 = ftell(fp), fgetline(&buf, &bufsize, &count, &buflen, fp, 0) != NULL) { for (bp = buf; *bp != ' '; bp++); /* strip old tag */ while (*bp == ' ') bp++; twice = 0; offs = ftell(fp); again: snprintf(o, sizeof o, "%s %s", tag(1), bp); if (ascncasecmp(bp, "UID COPY ", 9) == 0) { cp = &bp[9]; while (digitchar(*cp&0377)) cp++; if (*cp != ' ') goto fail; while (*cp == ' ') cp++; if ((newname = imap_strex(cp, NULL)) == NULL) goto fail; IMAP_OUT(o, MB_COMD, continue) while (mp->mb_active & MB_COMD) ok = imap_answer(mp, twice); if (response_status == RESPONSE_NO && twice++ == 0) goto trycreate; if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) { imap_copyuid(mp, NULL, imap_unquotestr(newname)); } } else if (ascncasecmp(bp, "UID STORE ", 10) == 0) { IMAP_OUT(o, MB_COMD, continue) while (mp->mb_active & MB_COMD) ok = imap_answer(mp, 1); if (ok == OKAY) gotcha++; } else if (ascncasecmp(bp, "APPEND ", 7) == 0) { if ((cp = strrchr(bp, '{')) == NULL) goto fail; octets = atol(&cp[1]) + 2; if ((newname = imap_strex(&bp[7], NULL)) == NULL) goto fail; IMAP_OUT(o, MB_COMD, continue) while (mp->mb_active & MB_COMD) { ok = imap_answer(mp, twice); if (response_type == RESPONSE_CONT) break; } if (ok == STOP) { if (twice++ == 0) { fseek(fp, offs, SEEK_SET); goto trycreate; } goto fail; } while (octets > 0) { n = octets > sizeof iob ? sizeof iob : octets; octets -= n; if (fread(iob, 1, n, fp) != n) goto fail; swrite1(&mp->mb_sock, iob, n, 1); } swrite(&mp->mb_sock, ""); while (mp->mb_active & MB_COMD) { ok = imap_answer(mp, 0); if (response_status == RESPONSE_NO && twice++ == 0) { fseek(fp, offs, SEEK_SET); goto trycreate; } } if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) { offs2 = ftell(fp); fseek(fp, offs1, SEEK_SET); if (imap_appenduid_cached(mp, fp) == STOP) { fseek(fp, offs2, SEEK_SET); goto fail; } } } else { fail: fprintf(stderr, "Invalid command in IMAP cache queue: \"%s\"\n", bp); rok = STOP; } continue; trycreate: snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), newname); IMAP_OUT(o, MB_COMD, continue) while (mp->mb_active & MB_COMD) ok = imap_answer(mp, 1); if (ok == OKAY) goto again; } fflush(fp); rewind(fp); ftruncate(fileno(fp), 0); if (gotcha) imap_close(mp); return rok; } static char * imap_strex(const char *cp, char **xp) { const char *cq; char *n; if (*cp != '"') return NULL; for (cq = &cp[1]; *cq; cq++) { if (*cq == '\\') cq++; else if (*cq == '"') break; } if (*cq != '"') return NULL; n = salloc(cq - cp + 2); memcpy(n, cp, cq - cp + 1); n[cq - cp + 1] = '\0'; if (xp) *xp = (char *)&cq[1]; return n; } static enum okay check_expunged(void) { if (expunged_messages > 0) { fprintf(stderr, "Command not executed - messages have been expunged\n"); return STOP; } return OKAY; } /*ARGSUSED*/ int cconnect(void *vp) { char *cp, *cq; int omsgCount = msgCount; if (mb.mb_type == MB_IMAP && mb.mb_sock.s_fd > 0) { fprintf(stderr, "Already connected.\n"); return 1; } unset_allow_undefined = 1; unset_internal("disconnected"); cp = protbase(mailname); if (strncmp(cp, "imap://", 7) == 0) cp += 7; else if (strncmp(cp, "imaps://", 8) == 0) cp += 8; if ((cq = strchr(cp, ':')) != NULL) *cq = '\0'; unset_internal(savecat("disconnected-", cp)); unset_allow_undefined = 0; if (mb.mb_type == MB_CACHE) { imap_setfile1(mailname, 0, edit, 1); if (msgCount > omsgCount) newmailinfo(omsgCount); } return 0; } int cdisconnect(void *vp) { int *msgvec = vp; if (mb.mb_type == MB_CACHE) { fprintf(stderr, "Not connected.\n"); return 1; } else if (mb.mb_type == MB_IMAP) { if (cached_uidvalidity(&mb) == 0) { fprintf(stderr, "The current mailbox is not cached.\n"); return 1; } } if (*msgvec) ccache(vp); assign("disconnected", ""); if (mb.mb_type == MB_IMAP) { sclose(&mb.mb_sock); imap_setfile1(mailname, 0, edit, 1); } return 0; } int ccache(void *vp) { int *msgvec = vp, *ip; struct message *mp; if (mb.mb_type != MB_IMAP) { fprintf(stderr, "Not connected to an IMAP server.\n"); return 1; } if (cached_uidvalidity(&mb) == 0) { fprintf(stderr, "The current mailbox is not cached.\n"); return 1; } for (ip = msgvec; *ip; ip++) { mp = &message[*ip-1]; if (!(mp->m_have & HAVE_BODY)) get_body(mp); } return 0; } #else /* !HAVE_SOCKETS */ #include "extern.h" static void noimap(void) { fprintf(stderr, catgets(catd, CATSET, 216, "No IMAP support compiled in.\n")); } int imap_setfile(const char *server, int newmail, int isedit) { noimap(); return -1; } enum okay imap_header(struct message *mp) { noimap(); return STOP; } enum okay imap_body(struct message *mp) { noimap(); return STOP; } void imap_getheaders(int bot, int top) { } void imap_quit(void) { noimap(); } /*ARGSUSED*/ int imap_imap(void *vp) { noimap(); return 1; } /*ARGSUSED*/ int imap_newmail(int dummy) { return 0; } /*ARGSUSED*/ enum okay imap_undelete(struct message *m, int n) { return STOP; } /*ARGSUSED*/ enum okay imap_unread(struct message *m, int n) { return STOP; } /*ARGSUSED*/ enum okay imap_append(const char *server, FILE *fp) { noimap(); return STOP; } /*ARGSUSED*/ void imap_folders(const char *name, int strip) { noimap(); } /*ARGSUSED*/ enum okay imap_remove(const char *name) { noimap(); return STOP; } /*ARGSUSED*/ enum okay imap_rename(const char *old, const char *new) { noimap(); return STOP; } enum okay imap_copy(struct message *m, int n, const char *name) { noimap(); return STOP; } /*ARGSUSED*/ enum okay imap_search1(const char *spec, int f) { return STOP; } int imap_thisaccount(const char *cp) { return 0; } enum okay imap_noop(void) { noimap(); return STOP; } /*ARGSUSED*/ int cconnect(void *vp) { noimap(); return 1; } /*ARGSUSED*/ int cdisconnect(void *vp) { noimap(); return 1; } /*ARGSUSED*/ int ccache(void *vp) { noimap(); return 1; } #endif /* HAVE_SOCKETS */ time_t imap_read_date_time(const char *cp) { time_t t; int i, year, month, day, hour, minute, second; int sign = -1; char buf[3]; /* * "25-Jul-2004 15:33:44 +0200" * | | | | | | * 0 5 10 15 20 25 */ if (cp[0] != '"' || strlen(cp) < 28 || cp[27] != '"') goto invalid; day = strtol(&cp[1], NULL, 10); for (i = 0; month_names[i]; i++) if (ascncasecmp(&cp[4], month_names[i], 3) == 0) break; if (month_names[i] == NULL) goto invalid; month = i + 1; year = strtol(&cp[8], NULL, 10); hour = strtol(&cp[13], NULL, 10); minute = strtol(&cp[16], NULL, 10); second = strtol(&cp[19], NULL, 10); if ((t = combinetime(year, month, day, hour, minute, second)) == (time_t)-1) goto invalid; switch (cp[22]) { case '-': sign = 1; break; case '+': break; default: goto invalid; } buf[2] = '\0'; buf[0] = cp[23]; buf[1] = cp[24]; t += strtol(buf, NULL, 10) * sign * 3600; buf[0] = cp[25]; buf[1] = cp[26]; t += strtol(buf, NULL, 10) * sign * 60; return t; invalid: time(&t); return t; } time_t imap_read_date(const char *cp) { time_t t; int year, month, day, i, tzdiff; struct tm *tmptr; char *xp, *yp; if (*cp == '"') cp++; day = strtol(cp, &xp, 10); if (day <= 0 || day > 31 || *xp++ != '-') return -1; for (i = 0; month_names[i]; i++) if (ascncasecmp(xp, month_names[i], 3) == 0) break; if (month_names[i] == NULL) return -1; month = i+1; if (xp[3] != '-') return -1; year = strtol(&xp[4], &yp, 10); if (year < 1970 || year > 2037 || yp != &xp[8]) return -1; if (yp[0] != '\0' && (yp[1] != '"' || yp[2] != '\0')) return -1; if ((t = combinetime(year, month, day, 0, 0, 0)) == (time_t)-1) return -1; tzdiff = t - mktime(gmtime(&t)); tmptr = localtime(&t); if (tmptr->tm_isdst > 0) tzdiff += 3600; t -= tzdiff; return t; } const char * imap_make_date_time(time_t t) { static char s[30]; struct tm *tmptr; int tzdiff, tzdiff_hour, tzdiff_min; tzdiff = t - mktime(gmtime(&t)); tzdiff_hour = (int)(tzdiff / 60); tzdiff_min = tzdiff_hour % 60; tzdiff_hour /= 60; tmptr = localtime(&t); if (tmptr->tm_isdst > 0) tzdiff_hour++; snprintf(s, sizeof s, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"", tmptr->tm_mday, month_names[tmptr->tm_mon], tmptr->tm_year + 1900, tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec, tzdiff_hour, tzdiff_min); return s; } char * imap_quotestr(const char *s) { char *n, *np; np = n = salloc(2 * strlen(s) + 3); *np++ = '"'; while (*s) { if (*s == '"' || *s == '\\') *np++ = '\\'; *np++ = *s++; } *np++ = '"'; *np = '\0'; return n; } char * imap_unquotestr(const char *s) { char *n, *np; if (*s != '"') return savestr(s); np = n = salloc(strlen(s) + 1); while (*++s) { if (*s == '\\') s++; else if (*s == '"') break; *np++ = *s; } *np = '\0'; return n; } heirloom-mailx-12.5/imap_gssapi.c000066400000000000000000000221541155563371200170430ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 2004 * Gunnar Ritter. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Gunnar Ritter * and his contributors. * 4. Neither the name of Gunnar Ritter nor the names of his contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Partially derived from sample code in: * * GSS-API Programming Guide * Part No: 816-1331-11 * Sun Microsystems, Inc. 4150 Network Circle Santa Clara, CA 95054 U.S.A. * * (c) 2002 Sun Microsystems */ /* * Copyright 1994 by OpenVision Technologies, Inc. * * Permission to use, copy, modify, distribute, and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appears in all copies and * that both that copyright notice and this permission notice appear in * supporting documentation, and that the name of OpenVision not be used * in advertising or publicity pertaining to distribution of the software * without specific, written prior permission. OpenVision makes no * representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied warranty. * * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)imap_gssapi.c 1.10 (gritter) 3/4/06"; #endif #endif /* not lint */ /* * Implementation of IMAP GSSAPI authentication according to RFC 1731. */ #ifdef USE_GSSAPI #ifndef GSSAPI_REG_INCLUDE #include #ifdef GSSAPI_OLD_STYLE #include #define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name #endif /* GSSAPI_OLD_STYLE */ #else /* GSSAPI_REG_INCLUDE */ #include #endif /* GSSAPI_REG_INCLUDE */ static void imap_gss_error1(const char *s, OM_uint32 code, int type); static void imap_gss_error(const char *s, OM_uint32 maj_stat, OM_uint32 min_stat); static void imap_gss_error1(const char *s, OM_uint32 code, int type) { OM_uint32 maj_stat, min_stat; gss_buffer_desc msg = GSS_C_EMPTY_BUFFER; OM_uint32 msg_ctx = 0; do { maj_stat = gss_display_status(&min_stat, code, type, GSS_C_NO_OID, &msg_ctx, &msg); if (maj_stat == GSS_S_COMPLETE) { fprintf(stderr, "GSS error: %s / %s\n", s, (char *)msg.value); if (msg.length != 0) gss_release_buffer(&min_stat, &msg); } else { fprintf(stderr, "GSS error: %s / unknown\n", s); break; } } while (msg_ctx); } static void imap_gss_error(const char *s, OM_uint32 maj_stat, OM_uint32 min_stat) { imap_gss_error1(s, maj_stat, GSS_C_GSS_CODE); imap_gss_error1(s, min_stat, GSS_C_MECH_CODE); } static enum okay imap_gss(struct mailbox *mp, char *user) { gss_buffer_desc send_tok, recv_tok, *token_ptr; gss_name_t target_name; gss_ctx_id_t gss_context; OM_uint32 maj_stat, min_stat, ret_flags; int conf_state; struct str in, out; FILE *queuefp = NULL; char *server, *cp; char o[LINESIZE]; enum okay ok = STOP; if (user == NULL && (user = getuser()) == NULL) return STOP; server = salloc(strlen(mp->mb_imap_account)); strcpy(server, mp->mb_imap_account); if (strncmp(server, "imap://", 7) == 0) server += 7; else if (strncmp(server, "imaps://", 8) == 0) server += 8; if ((cp = last_at_before_slash(server)) != NULL) server = &cp[1]; for (cp = server; *cp; cp++) *cp = lowerconv(*cp&0377); send_tok.value = salloc(send_tok.length = strlen(server) + 6); snprintf(send_tok.value, send_tok.length, "imap@%s", server); maj_stat = gss_import_name(&min_stat, &send_tok, GSS_C_NT_HOSTBASED_SERVICE, &target_name); if (maj_stat != GSS_S_COMPLETE) { imap_gss_error(send_tok.value, maj_stat, min_stat); return STOP; } token_ptr = GSS_C_NO_BUFFER; gss_context = GSS_C_NO_CONTEXT; maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, &gss_context, target_name, GSS_C_NO_OID, GSS_C_MUTUAL_FLAG|GSS_C_SEQUENCE_FLAG, 0, GSS_C_NO_CHANNEL_BINDINGS, token_ptr, NULL, &send_tok, &ret_flags, NULL); if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) { imap_gss_error("initializing GSS context", maj_stat, min_stat); gss_release_name(&min_stat, &target_name); return STOP; } snprintf(o, sizeof o, "%s AUTHENTICATE GSSAPI\r\n", tag(1)); IMAP_OUT(o, 0, return STOP); /* * No response data expected. */ imap_answer(mp, 1); if (response_type != RESPONSE_CONT) return STOP; while (maj_stat == GSS_S_CONTINUE_NEEDED) { /* * Pass token obtained from first gss_init_sec_context() call. */ cp = memtob64(send_tok.value, send_tok.length); gss_release_buffer(&min_stat, &send_tok); snprintf(o, sizeof o, "%s\r\n", cp); free(cp); IMAP_OUT(o, 0, return STOP); imap_answer(mp, 1); if (response_type != RESPONSE_CONT) return STOP; in.s = responded_text; in.l = strlen(responded_text); mime_fromb64(&in, &out, 0); recv_tok.value = out.s; recv_tok.length = out.l; token_ptr = &recv_tok; maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, &gss_context, target_name, GSS_C_NO_OID, GSS_C_MUTUAL_FLAG|GSS_C_SEQUENCE_FLAG, 0, GSS_C_NO_CHANNEL_BINDINGS, token_ptr, NULL, &send_tok, &ret_flags, NULL); if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) { imap_gss_error("initializing context", maj_stat, min_stat); gss_release_name(&min_stat, &target_name); return STOP; } free(out.s); } /* * Pass token obtained from second gss_init_sec_context() call. */ gss_release_name(&min_stat, &target_name); cp = memtob64(send_tok.value, send_tok.length); gss_release_buffer(&min_stat, &send_tok); snprintf(o, sizeof o, "%s\r\n", cp); free(cp); IMAP_OUT(o, 0, return STOP); /* * First octet: bit-mask with protection mechanisms. * Second to fourth octet: maximum message size in network byte order. * * This code currently does not care about the values. */ imap_answer(mp, 1); if (response_type != RESPONSE_CONT) return STOP; in.s = responded_text; in.l = strlen(responded_text); mime_fromb64(&in, &out, 0); recv_tok.value = out.s; recv_tok.length = out.l; maj_stat = gss_unwrap(&min_stat, gss_context, &recv_tok, &send_tok, &conf_state, NULL); if (maj_stat != GSS_S_COMPLETE) { imap_gss_error("unwrapping data", maj_stat, min_stat); return STOP; } free(out.s); /* * First octet: bit-mask with protection mechanisms (1 = no protection * mechanism). * Second to fourth octet: maximum message size in network byte order. * Fifth and following octets: user name string. */ o[0] = 1; o[1] = 0; o[2] = o[3] = 0377; snprintf(&o[4], sizeof o - 4, "%s", user); send_tok.value = o; send_tok.length = strlen(&o[4]) + 5; maj_stat = gss_wrap(&min_stat, gss_context, 0, GSS_C_QOP_DEFAULT, &send_tok, &conf_state, &recv_tok); if (maj_stat != GSS_S_COMPLETE) { imap_gss_error("wrapping data", maj_stat, min_stat); return STOP; } cp = memtob64(recv_tok.value, recv_tok.length); snprintf(o, sizeof o, "%s\r\n", cp); free(cp); IMAP_OUT(o, MB_COMD, return STOP); while (mp->mb_active & MB_COMD) ok = imap_answer(mp, 1); gss_delete_sec_context(&min_stat, &gss_context, &recv_tok); gss_release_buffer(&min_stat, &recv_tok); return ok; } #endif /* USE_GSSAPI */ heirloom-mailx-12.5/imap_search.c000066400000000000000000000410371155563371200170230ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 2004 * Gunnar Ritter. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Gunnar Ritter * and his contributors. * 4. Neither the name of Gunnar Ritter nor the names of his contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)imap_search.c 1.29 (gritter) 3/4/06"; #endif #endif /* not lint */ #include "config.h" #include "rcv.h" #include "extern.h" #include /* * Mail -- a mail program * * Client-side implementation of the IMAP SEARCH command. This is used * for folders not located on IMAP servers, or for IMAP servers that do * not implement the SEARCH command. */ static enum itoken { ITBAD, ITEOD, ITBOL, ITEOL, ITAND, ITSET, ITALL, ITANSWERED, ITBCC, ITBEFORE, ITBODY, ITCC, ITDELETED, ITDRAFT, ITFLAGGED, ITFROM, ITHEADER, ITKEYWORD, ITLARGER, ITNEW, ITNOT, ITOLD, ITON, ITOR, ITRECENT, ITSEEN, ITSENTBEFORE, ITSENTON, ITSENTSINCE, ITSINCE, ITSMALLER, ITSUBJECT, ITTEXT, ITTO, ITUID, ITUNANSWERED, ITUNDELETED, ITUNDRAFT, ITUNFLAGGED, ITUNKEYWORD, ITUNSEEN } itoken; static unsigned long inumber; static void *iargs[2]; static int needheaders; static struct itlex { const char *s_string; enum itoken s_token; } strings[] = { { "ALL", ITALL }, { "ANSWERED", ITANSWERED }, { "BCC", ITBCC }, { "BEFORE", ITBEFORE }, { "BODY", ITBODY }, { "CC", ITCC }, { "DELETED", ITDELETED }, { "DRAFT", ITDRAFT }, { "FLAGGED", ITFLAGGED }, { "FROM", ITFROM }, { "HEADER", ITHEADER }, { "KEYWORD", ITKEYWORD }, { "LARGER", ITLARGER }, { "NEW", ITNEW }, { "NOT", ITNOT }, { "OLD", ITOLD }, { "ON", ITON }, { "OR", ITOR }, { "RECENT", ITRECENT }, { "SEEN", ITSEEN }, { "SENTBEFORE", ITSENTBEFORE }, { "SENTON", ITSENTON }, { "SENTSINCE", ITSENTSINCE }, { "SINCE", ITSINCE }, { "SMALLER", ITSMALLER }, { "SUBJECT", ITSUBJECT }, { "TEXT", ITTEXT }, { "TO", ITTO }, { "UID", ITUID }, { "UNANSWERED", ITUNANSWERED }, { "UNDELETED", ITUNDELETED }, { "UNDRAFT", ITUNDRAFT }, { "UNFLAGGED", ITUNFLAGGED }, { "UNKEYWORD", ITUNKEYWORD }, { "UNSEEN", ITUNSEEN }, { NULL, ITBAD } }; static struct itnode { enum itoken n_token; unsigned long n_n; void *n_v; void *n_w; struct itnode *n_x; struct itnode *n_y; } *ittree; static const char *begin; static enum okay itparse(const char *spec, char **xp, int sub); static enum okay itscan(const char *spec, char **xp); static enum okay itsplit(const char *spec, char **xp); static enum okay itstring(void **tp, const char *spec, char **xp); static int itexecute(struct mailbox *mp, struct message *m, int c, struct itnode *n); static int matchfield(struct message *m, const char *field, const char *what); static int matchenvelope(struct message *m, const char *field, const char *what); static char *mkenvelope(struct name *np); static int matchmsg(struct message *m, const char *what, int withheader); static const char *around(const char *cp); enum okay imap_search(const char *spec, int f) { static char *lastspec; char *xp; int i; if (strcmp(spec, "()")) { free(lastspec); lastspec = sstrdup(spec); } else if (lastspec == NULL) { fprintf(stderr, "No last SEARCH criteria available.\n"); return STOP; } else spec = lastspec; begin = spec; if (imap_search1(spec, f) == OKAY) return OKAY; needheaders = 0; if (itparse(spec, &xp, 0) == STOP) return STOP; if (ittree == NULL) return OKAY; if (mb.mb_type == MB_IMAP && needheaders) imap_getheaders(1, msgCount); for (i = 0; i < msgCount; i++) { if (message[i].m_flag&MHIDDEN) continue; if (f == MDELETED || (message[i].m_flag&MDELETED) == 0) if (itexecute(&mb, &message[i], i+1, ittree)) mark(i+1, f); } return OKAY; } static enum okay itparse(const char *spec, char **xp, int sub) { int level = 0; struct itnode n, *z, *_ittree; enum okay ok; ittree = NULL; while ((ok = itscan(spec, xp)) == OKAY && itoken != ITBAD && itoken != ITEOD) { _ittree = ittree; memset(&n, 0, sizeof n); spec = *xp; switch (itoken) { case ITBOL: level++; continue; case ITEOL: if (--level == 0) { return OKAY; } if (level < 0) { if (sub > 0) { (*xp)--; return OKAY; } fprintf(stderr, "Excess in \")\".\n"); return STOP; } continue; case ITNOT: /* */ n.n_token = ITNOT; if (itparse(spec, xp, sub+1) == STOP) return STOP; spec = *xp; if ((n.n_x = ittree) == NULL) { fprintf(stderr, "Criterion for NOT missing: >>> %s <<<\n", around(*xp)); return STOP; } itoken = ITNOT; break; case ITOR: /* */ n.n_token = ITOR; if (itparse(spec, xp, sub+1) == STOP) return STOP; if ((n.n_x = ittree) == NULL) { fprintf(stderr, "First criterion for OR " "missing: >>> %s <<<\n", around(*xp)); return STOP; } spec = *xp; if (itparse(spec, xp, sub+1) == STOP) return STOP; spec = *xp; if ((n.n_y = ittree) == NULL) { fprintf(stderr, "Second criterion for OR " "missing: >>> %s <<<\n", around(*xp)); return STOP; } break; default: n.n_token = itoken; n.n_n = inumber; n.n_v = iargs[0]; n.n_w = iargs[1]; } ittree = _ittree; if (ittree == NULL) { ittree = salloc(sizeof *ittree); *ittree = n; } else { z = ittree; ittree = salloc(sizeof *ittree); ittree->n_token = ITAND; ittree->n_x = z; ittree->n_y = salloc(sizeof*ittree->n_y); *ittree->n_y = n; } if (sub && level == 0) break; } return ok; } static enum okay itscan(const char *spec, char **xp) { int i, n; while (spacechar(*spec&0377)) spec++; if (*spec == '(') { *xp = (char *)&spec[1]; itoken = ITBOL; return OKAY; } if (*spec == ')') { *xp = (char *)&spec[1]; itoken = ITEOL; return OKAY; } while (spacechar(*spec&0377)) spec++; if (*spec == '\0') { itoken = ITEOD; return OKAY; } for (i = 0; strings[i].s_string; i++) { n = strlen(strings[i].s_string); if (ascncasecmp(spec, strings[i].s_string, n) == 0 && (spacechar(spec[n]&0377) || spec[n] == '\0' || spec[n] == '(' || spec[n] == ')')) { itoken = strings[i].s_token; spec += n; while (spacechar(*spec&0377)) spec++; return itsplit(spec, xp); } } if (digitchar(*spec&0377)) { inumber = strtoul(spec, xp, 10); if (spacechar(**xp&0377) || **xp == '\0' || **xp == '(' || **xp == ')') { itoken = ITSET; return OKAY; } } fprintf(stderr, "Bad SEARCH criterion \""); while (*spec && !spacechar(*spec&0377) && *spec != '(' && *spec != ')') { putc(*spec&0377, stderr); spec++; } fprintf(stderr, "\": >>> %s <<<\n", around(*xp)); itoken = ITBAD; return STOP; } static enum okay itsplit(const char *spec, char **xp) { char *cp; time_t t; switch (itoken) { case ITBCC: case ITBODY: case ITCC: case ITFROM: case ITSUBJECT: case ITTEXT: case ITTO: /* */ needheaders++; return itstring(&iargs[0], spec, xp); case ITSENTBEFORE: case ITSENTON: case ITSENTSINCE: needheaders++; /*FALLTHRU*/ case ITBEFORE: case ITON: case ITSINCE: /* */ if (itstring(&iargs[0], spec, xp) != OKAY) return STOP; if ((t = imap_read_date(iargs[0])) == (time_t)-1) { fprintf(stderr, "Invalid date \"%s\": >>> %s <<<\n", (char *)iargs[0], around(*xp)); return STOP; } inumber = t; return OKAY; case ITHEADER: /* */ needheaders++; if (itstring(&iargs[0], spec, xp) != OKAY) return STOP; spec = *xp; return itstring(&iargs[1], spec, xp); case ITKEYWORD: case ITUNKEYWORD: /* */ if (itstring(&iargs[0], spec, xp) != OKAY) return STOP; if (asccasecmp(iargs[0], "\\Seen") == 0) inumber = MREAD; else if (asccasecmp(iargs[0], "\\Deleted") == 0) inumber = MDELETED; else if (asccasecmp(iargs[0], "\\Recent") == 0) inumber = MNEW; else if (asccasecmp(iargs[0], "\\Flagged") == 0) inumber = MFLAGGED; else if (asccasecmp(iargs[0], "\\Answered") == 0) inumber = MANSWERED; else if (asccasecmp(iargs[0], "\\Draft") == 0) inumber = MDRAFT; else inumber = 0; return OKAY; case ITLARGER: case ITSMALLER: /* */ if (itstring(&iargs[0], spec, xp) != OKAY) return STOP; inumber = strtoul(iargs[0], &cp, 10); if (spacechar(*cp&0377) || *cp == '\0') return OKAY; fprintf(stderr, "Invalid size: >>> %s <<<\n", around(*xp)); return STOP; case ITUID: /* */ fprintf(stderr, "Searching for UIDs is not supported: >>> %s <<<\n", around(*xp)); return STOP; default: *xp = (char *)spec; return OKAY; } } static enum okay itstring(void **tp, const char *spec, char **xp) { int inquote = 0; char *ap; while (spacechar(*spec&0377)) spec++; if (*spec == '\0' || *spec == '(' || *spec == ')') { fprintf(stderr, "Missing string argument: >>> %s <<<\n", around(&(*xp)[spec - *xp])); return STOP; } ap = *tp = salloc(strlen(spec) + 1); *xp = (char *)spec; do { if (inquote && **xp == '\\') *ap++ = *(*xp)++; else if (**xp == '"') inquote = !inquote; else if (!inquote && (spacechar(**xp&0377) || **xp == '(' || **xp == ')')) { *ap++ = '\0'; break; } *ap++ = **xp; } while (*(*xp)++); return OKAY; } static int itexecute(struct mailbox *mp, struct message *m, int c, struct itnode *n) { char *cp, *line = NULL; size_t linesize = 0; FILE *ibuf; if (n == NULL) { fprintf(stderr, "Internal error: Empty node in SEARCH tree.\n"); return 0; } switch (n->n_token) { case ITBEFORE: case ITON: case ITSINCE: if (m->m_time == 0 && (m->m_flag&MNOFROM) == 0 && (ibuf = setinput(mp, m, NEED_HEADER)) != NULL) { if (readline(ibuf, &line, &linesize) > 0) m->m_time = unixtime(line); free(line); } break; case ITSENTBEFORE: case ITSENTON: case ITSENTSINCE: if (m->m_date == 0) if ((cp = hfield("date", m)) != NULL) m->m_date = rfctime(cp); break; default: break; } switch (n->n_token) { default: fprintf(stderr, "Internal SEARCH error: Lost token %d\n", n->n_token); return 0; case ITAND: return itexecute(mp, m, c, n->n_x) & itexecute(mp, m, c, n->n_y); case ITSET: return c == n->n_n; case ITALL: return 1; case ITANSWERED: return (m->m_flag&MANSWERED) != 0; case ITBCC: return matchenvelope(m, "bcc", n->n_v); case ITBEFORE: return m->m_time < n->n_n; case ITBODY: return matchmsg(m, n->n_v, 0); case ITCC: return matchenvelope(m, "cc", n->n_v); case ITDELETED: return (m->m_flag&MDELETED) != 0; case ITDRAFT: return (m->m_flag&MDRAFTED) != 0; case ITFLAGGED: return (m->m_flag&MFLAGGED) != 0; case ITFROM: return matchenvelope(m, "from", n->n_v); case ITHEADER: return matchfield(m, n->n_v, n->n_w); case ITKEYWORD: return (m->m_flag & n->n_n) != 0; case ITLARGER: return m->m_xsize > n->n_n; case ITNEW: return (m->m_flag&(MNEW|MREAD)) == MNEW; case ITNOT: return !itexecute(mp, m, c, n->n_x); case ITOLD: return (m->m_flag&MNEW) == 0; case ITON: return m->m_time >= n->n_n && m->m_time < n->n_n + 86400; case ITOR: return itexecute(mp, m, c, n->n_x) | itexecute(mp, m, c, n->n_y); case ITRECENT: return (m->m_flag&MNEW) != 0; case ITSEEN: return (m->m_flag&MREAD) != 0; case ITSENTBEFORE: return m->m_date < n->n_n; case ITSENTON: return m->m_date >= n->n_n && m->m_date < n->n_n + 86400; case ITSENTSINCE: return m->m_date >= n->n_n; case ITSINCE: return m->m_time >= n->n_n; case ITSMALLER: return m->m_xsize < n->n_n; case ITSUBJECT: return matchfield(m, "subject", n->n_v); case ITTEXT: return matchmsg(m, n->n_v, 1); case ITTO: return matchenvelope(m, "to", n->n_v); case ITUNANSWERED: return (m->m_flag&MANSWERED) == 0; case ITUNDELETED: return (m->m_flag&MDELETED) == 0; case ITUNDRAFT: return (m->m_flag&MDRAFTED) == 0; case ITUNFLAGGED: return (m->m_flag&MFLAGGED) == 0; case ITUNKEYWORD: return (m->m_flag & n->n_n) == 0; case ITUNSEEN: return (m->m_flag&MREAD) == 0; } } static int matchfield(struct message *m, const char *field, const char *what) { struct str in, out; int i; if ((in.s = hfield(imap_unquotestr(field), m)) == NULL) return 0; in.l = strlen(in.s); mime_fromhdr(&in, &out, TD_ICONV); what = imap_unquotestr(what); i = substr(out.s, what); free(out.s); return i; } static int matchenvelope(struct message *m, const char *field, const char *what) { struct name *np; char *cp; if ((cp = hfield(imap_unquotestr(field), m)) == NULL) return 0; what = imap_unquotestr(what); np = sextract(cp, GFULL); while (np) { if (substr(np->n_name, what)) return 1; if (substr(mkenvelope(np), what)) return 1; np = np->n_flink; } return 0; } static char * mkenvelope(struct name *np) { size_t epsize; char *ep; char *realname = NULL, *sourceaddr = NULL, *localpart = NULL, *domainpart = NULL, *cp, *rp, *xp, *ip; struct str in, out; int level = 0, hadphrase = 0; in.s = np->n_fullname; in.l = strlen(in.s); mime_fromhdr(&in, &out, TD_ICONV); rp = ip = ac_alloc(strlen(out.s) + 1); for (cp = out.s; *cp; cp++) { switch (*cp) { case '"': while (*cp) { if (*++cp == '"') break; if (*cp == '\\' && cp[1]) cp++; *rp++ = *cp; } break; case '<': while (cp > out.s && blankchar(cp[-1]&0377)) cp--; rp = ip; xp = out.s; if (xp < &cp[-1] && *xp == '"' && cp[-1] == '"') { xp++; cp--; } while (xp < cp) *rp++ = *xp++; hadphrase = 1; goto done; case '(': if (level++) goto dfl; if (hadphrase++ == 0) rp = ip; break; case ')': if (--level) goto dfl; break; case '\\': if (level && cp[1]) cp++; goto dfl; default: dfl: *rp++ = *cp; } } done: *rp = '\0'; if (hadphrase) realname = ip; free(out.s); localpart = savestr(np->n_name); if ((cp = strrchr(localpart, '@')) != NULL) { *cp = '\0'; domainpart = &cp[1]; } ep = salloc(epsize = strlen(np->n_fullname) * 2 + 40); snprintf(ep, epsize, "(%s %s %s %s)", realname ? imap_quotestr(realname) : "NIL", sourceaddr ? imap_quotestr(sourceaddr) : "NIL", localpart ? imap_quotestr(localpart) : "NIL", domainpart ? imap_quotestr(domainpart) : "NIL"); ac_free(ip); return ep; } static int matchmsg(struct message *m, const char *what, int withheader) { char *tempFile, *line = NULL; size_t linesize, linelen, count; FILE *fp; int yes = 0; if ((fp = Ftemp(&tempFile, "Ra", "w+", 0600, 1)) == NULL) return 0; rm(tempFile); Ftfree(&tempFile); if (send(m, fp, NULL, NULL, SEND_TOSRCH, NULL) < 0) goto out; fflush(fp); rewind(fp); count = fsize(fp); line = smalloc(linesize = LINESIZE); linelen = 0; if (!withheader) while (fgetline(&line, &linesize, &count, &linelen, fp, 0)) if (*line == '\n') break; what = imap_unquotestr(what); while (fgetline(&line, &linesize, &count, &linelen, fp, 0)) if (substr(line, what)) { yes = 1; break; } out: free(line); Fclose(fp); return yes; } #define SURROUNDING 16 static const char * around(const char *cp) { int i; static char ab[2*SURROUNDING+1]; for (i = 0; i < SURROUNDING && cp > begin; i++) cp--; for (i = 0; i < sizeof ab - 1; i++) ab[i] = *cp++; ab[i] = '\0'; return ab; } heirloom-mailx-12.5/junk.c000066400000000000000000000717271155563371200155300ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 2004 * Gunnar Ritter. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Gunnar Ritter * and his contributors. * 4. Neither the name of Gunnar Ritter nor the names of his contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)junk.c 1.75 (gritter) 9/14/08"; #endif #endif /* not lint */ #include "config.h" #include "rcv.h" #include #include #include #include #include #include #ifdef HAVE_MMAP #include #else /* !HAVE_MMAP */ #define mmap(a, b, c, d, e, f) MAP_FAILED #define munmap(a, b) #endif /* !HAVE_MMAP */ #ifndef HAVE_MREMAP #define mremap(a, b, c, d) MAP_FAILED #endif /* !HAVE_MREMAP */ #ifndef MAP_FAILED #define MAP_FAILED ((void *)-1) #endif /* !MAP_FAILED */ #include "extern.h" #include "md5.h" /* * Mail -- a mail program * * Junk classification, mostly according to Paul Graham's "A Plan for Spam", * August 2002, , and his "Better * Bayesian Filtering", January 2003, . * * Chained tokens according to Jonathan A. Zdziarski's "Advanced Language * Classification using Chained Tokens", February 2004, * . */ #define DFL .40 #define THR .9 #define MID .5 #define MAX2 0x0000ffff #define MAX3 0x00ffffffUL #define MAX4 0xffffffffUL /* * The dictionary consists of two files forming a hash table. The hash * consists of the first 56 bits of the result of applying MD5 to the * input word. This scheme ensures that collisions are unlikely enough * to make junk detection work; according to the birthday paradox, a * 50 % probability for one single collision is reached at 2^28 entries. * * To make the chain structure independent from input, the MD5 input is * xor'ed with a random number. This makes it impossible that someone uses * a carefully crafted message for a denial-of-service attack against the * database. */ #define SIZEOF_node 17 #define OF_node_hash 0 /* first 32 bits of MD5 of word|mangle */ #define OF_node_next 4 /* bit-negated table index of next node */ #define OF_node_good 8 /* number of times this appeared in good msgs */ #define OF_node_bad 11 /* number of times this appeared in bad msgs */ #define OF_node_prob_O 14 /* table_version<1: precomputed probability */ #define OF_node_hash2 14 /* upper 3 bytes of MD5 hash */ static char *nodes; #define SIZEOF_super 262164 #define OF_super_size 0 /* allocated nodes in the chain file */ #define OF_super_used 4 /* used nodes in the chain file */ #define OF_super_ngood 8 /* number of good messages scanned so far */ #define OF_super_nbad 12 /* number of bad messages scanned so far */ #define OF_super_mangle 16 /* used to mangle the MD5 input */ #define OF_super_bucket 20 /* 65536 bit-negated node indices */ #define SIZEOF_entry 4 static char *super; static size_t super_mmapped; static size_t nodes_mmapped; static int rw_map; static int chained_tokens; /* * Version history * --------------- * 0 Initial version * 1 Fixed the mangling; it was ineffective in version 0. * Hash extended to 56 bits. */ static int table_version; #define current_table_version 1 #define get(e) \ ((unsigned)(((char *)(e))[0]&0377) + \ ((unsigned)(((char *)(e))[1]&0377) << 8) + \ ((unsigned)(((char *)(e))[2]&0377) << 16)) #define put(e, n) \ (((char *)(e))[0] = (n) & 0x0000ff, \ ((char *)(e))[1] = ((n) & 0x00ff00) >> 8, \ ((char *)(e))[2] = ((n) & 0xff0000) >> 16) #define f2s(d) (smin(((unsigned)((d) * MAX3)), MAX3)) #define s2f(s) ((float)(s) / MAX3) #define getn(p) \ ((unsigned long)(((char *)(p))[0]&0377) + \ ((unsigned long)(((char *)(p))[1]&0377) << 8) + \ ((unsigned long)(((char *)(p))[2]&0377) << 16) + \ ((unsigned long)(((char *)(p))[3]&0377) << 24)) #define putn(p, n) \ (((char *)(p))[0] = (n) & 0x000000ffUL, \ ((char *)(p))[1] = ((n) & 0x0000ff00UL) >> 8, \ ((char *)(p))[2] = ((n) & 0x00ff0000UL) >> 16, \ ((char *)(p))[3] = ((n) & 0xff000000UL) >> 24) struct lexstat { char *save; int price; int url; int lastc; int hadamp; enum loc { FROM_LINE = 0, HEADER = 1, BODY = 2 } loc; enum html { HTML_NONE = 0, HTML_TEXT = 1, HTML_TAG = 2, HTML_SKIP = 3 } html; char tag[8]; char *tagp; char field[LINESIZE]; }; #define constituent(c, b, i, price, hadamp) \ ((c) & 0200 || alnumchar(c) || (c) == '\'' || (c) == '"' || \ (c) == '$' || (c) == '!' || (c) == '_' || \ (c) == '#' || (c) == '%' || (c) == '&' || \ ((c) == ';' && hadamp) || \ ((c) == '-' && !(price)) || \ (((c) == '.' || (c) == ',' || (c) == '/') && \ (i) > 0 && digitchar((b)[(i)-1]&0377))) #define url_xchar(c) \ (((c)&0200) == 0 && ((c)&037) != (c) && (c) != 0177 && \ !spacechar(c) && (c) != '{' && (c) != '}' && (c) != '|' && \ (c) != '\\' && (c) != '^' && (c) != '~' && (c) != '[' && \ (c) != ']' && (c) != '`' && (c) != '<' && (c) != '>' && \ (c) != '#' && (c) != '"') enum db { SUPER = 0, NODES = 1 }; enum entry { GOOD = 0, BAD = 1 }; static const char README1[] = "\ This is a junk mail database maintained by mailx(1). It does not contain any\n\ of the actual words found in your messages. Instead, parts of MD5 hashes are\n\ used for lookup. It is thus possible to tell if some given word was likely\n\ contained in your mail from examining this data, at best.\n"; static const char README2[] = "\n\ The database files are stored in compress(1) format by default. This saves\n\ some space, but leads to higher processor usage when the database is read\n\ or updated. You can use uncompress(1) on these files if you prefer to store\n\ them in flat form.\n"; static int verbose; static int _debug; static FILE *sfp, *nfp; static char *sname, *nname; static enum okay getdb(int rw); static void putdb(void); static void relsedb(void); static FILE *dbfp(enum db db, int rw, int *compressed, char **fn); static char *lookup(unsigned long h1, unsigned long h2, int create); static unsigned long grow(unsigned long size); static char *nextword(char **buf, size_t *bufsize, size_t *count, FILE *fp, struct lexstat *sp, int *stop); static void join(char **buf, size_t *bufsize, const char *s1, const char *s2); static void add(const char *word, enum entry entry, struct lexstat *sp, int incr); static enum okay scan(struct message *m, enum entry entry, void (*func)(const char *, enum entry, struct lexstat *, int), int arg); static void recompute(void); static float getprob(char *n); static int insert(int *msgvec, enum entry entry, int incr); static void clsf(struct message *m); static void rate(const char *word, enum entry entry, struct lexstat *sp, int unused); static void dbhash(const char *word, unsigned long *h1, unsigned long *h2); static void mkmangle(void); static enum okay getdb(int rw) { void *zp = NULL; long n; int compressed; chained_tokens = value("chained-junk-tokens") != NULL; if ((sfp = dbfp(SUPER, rw, &compressed, &sname)) == (FILE *)-1) return STOP; if (sfp && !compressed) { super = mmap(NULL, SIZEOF_super, rw!=O_RDONLY ? PROT_READ|PROT_WRITE : PROT_READ, MAP_SHARED, fileno(sfp), 0); if (super != MAP_FAILED) { super_mmapped = SIZEOF_super; goto skip; } } super_mmapped = 0; super = smalloc(SIZEOF_super); if (sfp) { if (compressed) zp = zalloc(sfp); if ((compressed ? zread(zp, super, SIZEOF_super) != SIZEOF_super : fread(super, 1, SIZEOF_super, sfp) != SIZEOF_super) || ferror(sfp)) { fprintf(stderr, "Error reading junk mail database.\n"); memset(super, 0, SIZEOF_super); mkmangle(); if (compressed) zfree(zp); Fclose(sfp); sfp = NULL; } else if (compressed) zfree(zp); } else { memset(super, 0, SIZEOF_super); mkmangle(); } skip: if ((n = getn(&super[OF_super_size])) == 0) { n = 1; putn(&super[OF_super_size], 1); } if (sfp && (nfp = dbfp(NODES, rw, &compressed, &nname)) != NULL) { if (nfp == (FILE *)-1) { relsedb(); return STOP; } } rw_map = rw; if (sfp && nfp && !compressed) { nodes = mmap(NULL, n * SIZEOF_node, rw!=O_RDONLY ? PROT_READ|PROT_WRITE : PROT_READ, MAP_SHARED, fileno(nfp), 0); if (nodes != MAP_FAILED) { nodes_mmapped = n * SIZEOF_node; return OKAY; } } nodes_mmapped = 0; nodes = smalloc(n * SIZEOF_node); if (sfp && nfp) { if (compressed) zp = zalloc(nfp); if ((compressed ? zread(zp, nodes, n * SIZEOF_node) != n * SIZEOF_node : fread(nodes, 1, n * SIZEOF_node, nfp) != n * SIZEOF_node) || ferror(nfp)) { fprintf(stderr, "Error reading junk mail database.\n"); memset(nodes, 0, n * SIZEOF_node); memset(super, 0, SIZEOF_super); mkmangle(); putn(&super[OF_super_size], n); } if (compressed) zfree(zp); Fclose(nfp); nfp = NULL; } else memset(nodes, 0, n * SIZEOF_node); if (sfp) { Fclose(sfp); sfp = NULL; } return OKAY; } static void putdb(void) { void *zp; int scomp, ncomp; if (!super_mmapped && (sfp = dbfp(SUPER, O_WRONLY, &scomp, &sname)) == NULL || sfp == (FILE *)-1) return; if (!nodes_mmapped && (nfp = dbfp(NODES, O_WRONLY, &ncomp, &nname)) == NULL || nfp == (FILE *)-1) return; if (super_mmapped == 0 || nodes_mmapped == 0) holdint(); /* * Use utime() with mmap() since Linux does not update st_mtime * reliably otherwise. */ if (super_mmapped) utime(sname, NULL); else if (scomp) { zp = zalloc(sfp); zwrite(zp, super, SIZEOF_super); zfree(zp); trunc(sfp); } else fwrite(super, 1, SIZEOF_super, sfp); if (nodes_mmapped) utime(nname, NULL); else if (ncomp) { zp = zalloc(nfp); zwrite(zp, nodes, getn(&super[OF_super_size]) * SIZEOF_node); zfree(zp); trunc(nfp); } else fwrite(nodes, 1, getn(&super[OF_super_size]) * SIZEOF_node, nfp); if (super_mmapped == 0 || nodes_mmapped == 0) relseint(); } static void relsedb(void) { if (super_mmapped) { munmap(super, super_mmapped); super_mmapped = 0; } else free(super); if (nodes_mmapped) { munmap(nodes, nodes_mmapped); nodes_mmapped = 0; } else free(nodes); if (sfp && sfp != (FILE *)-1) { Fclose(sfp); sfp = NULL; } if (nfp && nfp != (FILE *)-1) { Fclose(nfp); nfp = NULL; } } static FILE * dbfp(enum db db, int rw, int *compressed, char **fn) { FILE *fp, *rp; char *dir; struct flock flp; char *sfx[][2] = { { "super", "nodes" }, { "super1", "nodes1" } }; char **sf; char *zfx[][2] = { { "super.Z", "nodes.Z" }, { "super1.Z", "nodes1.Z" } }; char **zf; int n; if ((dir = value("junkdb")) == NULL) { fprintf(stderr, "No junk mail database specified. " "Set the junkdb variable.\n"); return (FILE *)-1; } dir = expand(dir); if (makedir(dir) == STOP) { fprintf(stderr, "Cannot create directory \"%s\"\n.", dir); return (FILE *)-1; } if (rw!=O_WRONLY) table_version = current_table_version; loop: sf = sfx[table_version]; zf = zfx[table_version]; *fn = salloc((n = strlen(dir)) + 40); strcpy(*fn, dir); (*fn)[n] = '/'; *compressed = 0; strcpy(&(*fn)[n+1], sf[db]); if ((fp = Fopen(*fn, rw!=O_RDONLY ? "r+" : "r")) != NULL) goto okay; *compressed = 1; strcpy(&(*fn)[n+1], zf[db]); if ((fp = Fopen(*fn, rw ? "r+" : "r")) == NULL && rw==O_WRONLY ? (fp = Fopen(*fn, "w+")) == NULL : 0) { fprintf(stderr, "Cannot open junk mail database \"%s\".\n",*fn); return NULL; } if (rw==O_WRONLY) { strcpy(&(*fn)[n+1], "README"); if (access(*fn, F_OK) < 0 && (rp = Fopen(*fn, "w")) != NULL) { fputs(README1, rp); fputs(README2, rp); Fclose(rp); } } else if (fp == NULL) { if (table_version > 0) { table_version--; goto loop; } else table_version = current_table_version; } okay: if (fp) { flp.l_type = rw!=O_RDONLY ? F_WRLCK : F_RDLCK; flp.l_start = 0; flp.l_len = 0; flp.l_whence = SEEK_SET; fcntl(fileno(fp), F_SETLKW, &flp); } return fp; } static char * lookup(unsigned long h1, unsigned long h2, int create) { char *n, *lastn = NULL; unsigned long c, lastc = MAX4, used, size; used = getn(&super[OF_super_used]); size = getn(&super[OF_super_size]); c = ~getn(&super[OF_super_bucket + (h1&MAX2)*SIZEOF_entry]); n = &nodes[c*SIZEOF_node]; while (c < used) { if (getn(&n[OF_node_hash]) == h1 && (table_version < 1 ? 1 : get(&n[OF_node_hash2]) == h2)) return n; lastc = c; lastn = n; c = ~getn(&n[OF_node_next]); n = &nodes[c*SIZEOF_node]; } if (create) { if (used >= size) { if ((size = grow(size)) == 0) return NULL; lastn = &nodes[lastc*SIZEOF_node]; } putn(&super[OF_super_used], used+1); n = &nodes[used*SIZEOF_node]; putn(&n[OF_node_hash], h1); put(&n[OF_node_hash2], h2); if (lastc < used) putn(&lastn[OF_node_next], ~used); else putn(&super[OF_super_bucket + (h1&MAX2)*SIZEOF_entry], ~used); return n; } else return NULL; } static unsigned long grow(unsigned long size) { unsigned long incr, newsize; void *onodes; incr = size > MAX2 ? MAX2 : size; newsize = size + incr; if (newsize > MAX4-MAX2) { oflo: fprintf(stderr, "Junk mail database overflow.\n"); return 0; } if (nodes_mmapped) { if (lseek(fileno(nfp), newsize*SIZEOF_node-1, SEEK_SET) == (off_t)-1 || write(fileno(nfp),"\0",1) != 1) goto oflo; onodes = nodes; if ((nodes = mremap(nodes, nodes_mmapped, newsize*SIZEOF_node, MREMAP_MAYMOVE)) == MAP_FAILED) { if ((nodes = mmap(NULL, newsize*SIZEOF_node, rw_map!=O_RDONLY ? PROT_READ|PROT_WRITE : PROT_READ, MAP_SHARED, fileno(nfp), 0)) == MAP_FAILED) { nodes = onodes; goto oflo; } munmap(onodes, nodes_mmapped); } nodes_mmapped = newsize*SIZEOF_node; } else { nodes = srealloc(nodes, newsize*SIZEOF_node); memset(&nodes[size*SIZEOF_node], 0, incr*SIZEOF_node); } size = newsize; putn(&super[OF_super_size], size); return size; } #define SAVE(c) { \ if (i+j >= (long)*bufsize-4) \ *buf = srealloc(*buf, *bufsize += 32); \ (*buf)[j+i] = (c); \ i += (*buf)[j+i] != '\0'; \ } static char * nextword(char **buf, size_t *bufsize, size_t *count, FILE *fp, struct lexstat *sp, int *stop) { int c, i, j, k; char *cp, *cq; loop: *stop = 0; sp->hadamp = 0; if (sp->save) { i = j = 0; for (cp = sp->save; *cp; cp++) { SAVE(*cp&0377) } SAVE('\0') free(sp->save); sp->save = NULL; goto out; } if (sp->loc == FROM_LINE) while (*count > 0 && (c = getc(fp)) != EOF) { sp->lastc = c; if (c == '\n') { sp->loc = HEADER; break; } } i = 0; j = 0; if (sp->loc == HEADER && sp->field[0]) { field: cp = sp->field; do { c = *cp&0377; SAVE(c) cp++; } while (*cp); j = i; i = 0; } if (sp->price) { sp->price = 0; SAVE('$') } while (*count > 0 && (c = getc(fp)) != EOF) { (*count)--; if (c == '\0' && table_version >= 1) { sp->loc = HEADER; sp->lastc = '\n'; *stop = 1; continue; } if (c == '\b' && table_version >= 1) { sp->html = HTML_TEXT; continue; } if (c == '<' && sp->html == HTML_TEXT) { sp->html = HTML_TAG; sp->tagp = sp->tag; continue; } if (sp->html == HTML_TAG) { if (spacechar(c)) { *sp->tagp = '\0'; if (!asccasecmp(sp->tag, "a") || !asccasecmp(sp->tag, "img") || !asccasecmp(sp->tag, "font") || !asccasecmp(sp->tag, "span") || !asccasecmp(sp->tag, "meta") || !asccasecmp(sp->tag, "table") || !asccasecmp(sp->tag, "tr") || !asccasecmp(sp->tag, "td") || !asccasecmp(sp->tag, "p")) sp->html = HTML_TEXT; else sp->html = HTML_SKIP; } else if (c == '>') { sp->html = HTML_TEXT; continue; } else { if (sp->tagp - sp->tag < sizeof sp->tag - 1) *sp->tagp++ = c; continue; } } if (sp->html == HTML_SKIP) { if (c == '>') sp->html = HTML_TEXT; continue; } if (c == '$' && i == 0) sp->price = 1; if (sp->loc == HEADER && sp->lastc == '\n') { if (!spacechar(c)) { k = 0; while (k < sizeof sp->field - 3) { sp->field[k++] = c; if (*count <= 0 || (c = getc(fp)) == EOF) break; if (spacechar(c) || c == ':') { ungetc(c, fp); break; } sp->lastc = c; (*count)--; } sp->field[k++] = '*'; sp->field[k] = '\0'; j = 0; *stop = 1; goto field; } else if (c == '\n') { j = 0; sp->loc = BODY; sp->html = HTML_NONE; *stop = 1; } } if (sp->url) { if (!url_xchar(c)) { sp->url = 0; cp = sp->save = smalloc(i+6); for (cq = "HOST*"; *cq; cq++) *cp++ = *cq; for (cq = &(*buf)[j]; *cq != ':'; cq++); cq += 3; /* skip "://" */ while (cq < &(*buf)[i+j] && (alnumchar(*cq&0377) || *cq == '.' || *cq == '-')) *cp++ = *cq++; *cp = '\0'; *stop = 1; break; } SAVE(c) } else if (constituent(c, *buf, i+j, sp->price, sp->hadamp) || sp->loc == HEADER && c == '.' && asccasecmp(sp->field, "subject*")) { if (c == '&') sp->hadamp = 1; SAVE(c) } else if (i > 0 && c == ':' && *count > 2) { if ((c = getc(fp)) != '/') { ungetc(c, fp); break; } (*count)--; if ((c = getc(fp)) != '/') { ungetc(c, fp); break; } (*count)--; sp->url = 1; SAVE('\0') cp = savestr(*buf); j = i = 0; for (cq = "URL*"; *cq; cq++) { SAVE(*cq&0377) } j = i; i = 0; do { if (alnumchar(*cp&0377)) { SAVE(*cp&0377) } else i = 0; } while (*++cp); for (cq = "://"; *cq; cq++) { SAVE(*cq&0377) } } else if (i > 1 && ((*buf)[i+j-1] == ',' || (*buf)[i+j-1] == '.') && !digitchar(c)) { i--; ungetc(c, fp); (*count)++; break; } else if (i > 0) { sp->lastc = c; break; } sp->lastc = c; } out: if (i > 0) { SAVE('\0') c = 0; for (k = 0; k < i; k++) if (digitchar((*buf)[k+j]&0377)) c++; else if (!alphachar((*buf)[k+j]&0377) && (*buf)[k+j] != '$') { c = 0; break; } if (c == i) goto loop; /* * Including the results of other filtering software (the * 'X-Spam' fields) might seem tempting, but will also rate * their false negatives good with this filter. Therefore * these fields are ignored. * * Handling 'Received' fields is difficult since they include * lots of both useless and interesting words for our purposes. */ if (sp->loc == HEADER && (asccasecmp(sp->field, "message-id*") == 0 || asccasecmp(sp->field, "references*") == 0 || asccasecmp(sp->field, "in-reply-to*") == 0 || asccasecmp(sp->field, "status*") == 0 || asccasecmp(sp->field, "x-status*") == 0 || asccasecmp(sp->field, "date*") == 0 || asccasecmp(sp->field, "delivery-date*") == 0 || ascncasecmp(sp->field, "x-spam", 6) == 0 || ascncasecmp(sp->field, "x-pstn", 6) == 0 || ascncasecmp(sp->field, "x-scanned", 9) == 0 || asccasecmp(sp->field, "received*") == 0 && ((2*c > i) || i < 4 || asccasestr(*buf, "localhost") != NULL))) goto loop; return *buf; } return NULL; } #define JOINCHECK if (i >= *bufsize) \ *buf = srealloc(*buf, *bufsize += 32) static void join(char **buf, size_t *bufsize, const char *s1, const char *s2) { int i = 0; while (*s1) { JOINCHECK; (*buf)[i++] = *s1++; } JOINCHECK; (*buf)[i++] = ' '; do { JOINCHECK; (*buf)[i++] = *s2; } while (*s2++); } /*ARGSUSED3*/ static void add(const char *word, enum entry entry, struct lexstat *sp, int incr) { unsigned c; unsigned long h1, h2; char *n; dbhash(word, &h1, &h2); if ((n = lookup(h1, h2, 1)) != NULL) { switch (entry) { case GOOD: c = get(&n[OF_node_good]); if (incr>0 && c=-incr) { c += incr; put(&n[OF_node_good], c); } break; case BAD: c = get(&n[OF_node_bad]); if (incr>0 && c=-incr) { c += incr; put(&n[OF_node_bad], c); } break; } } } static enum okay scan(struct message *m, enum entry entry, void (*func)(const char *, enum entry, struct lexstat *, int), int arg) { FILE *fp; char *buf0 = NULL, *buf1 = NULL, *buf2 = NULL, **bp, *cp; size_t bufsize0 = 0, bufsize1 = 0, bufsize2 = 0, *zp, count; struct lexstat *sp; int stop; if ((fp = Ftemp(&cp, "Ra", "w+", 0600, 1)) == NULL) { perror("tempfile"); return STOP; } rm(cp); Ftfree(&cp); if (send(m, fp, NULL, NULL, SEND_TOFLTR, NULL) < 0) { Fclose(fp); return STOP; } fflush(fp); rewind(fp); sp = scalloc(1, sizeof *sp); count = fsize(fp); bp = &buf0; zp = &bufsize0; while (nextword(bp, zp, &count, fp, sp, &stop) != NULL) { (*func)(*bp, entry, sp, arg); if (chained_tokens && buf0 && *buf0 && buf1 && *buf1 && !stop) { join(&buf2, &bufsize2, bp == &buf1 ? buf0 : buf1, *bp); (*func)(buf2, entry, sp, arg); } bp = bp == &buf1 ? &buf0 : &buf1; zp = zp == &bufsize1 ? &bufsize0 : &bufsize1; } free(buf0); free(buf1); free(buf2); free(sp); Fclose(fp); return OKAY; } static void recompute(void) { unsigned long used, i; unsigned s; char *n; float p; used = getn(&super[OF_super_used]); for (i = 0; i < used; i++) { n = &nodes[i*SIZEOF_node]; p = getprob(n); s = f2s(p); put(&n[OF_node_prob_O], s); } } static float getprob(char *n) { unsigned long ngood, nbad; unsigned g, b; float p, BOT, TOP; ngood = getn(&super[OF_super_ngood]); nbad = getn(&super[OF_super_nbad]); if (ngood + nbad >= 18000) { BOT = .0001; TOP = .9999; } else if (ngood + nbad >= 9000) { BOT = .001; TOP = .999; } else { BOT = .01; TOP = .99; } g = get(&n[OF_node_good]) * 2; b = get(&n[OF_node_bad]); if (g + b >= 5) { p = smin(1.0, nbad ? (float)b/nbad : 0.0) / (smin(1.0, ngood ? (float)g/ngood : 0.0) + smin(1.0, nbad ? (float)b/nbad : 0.0)); p = smin(TOP, p); p = smax(BOT, p); if (p == TOP && b <= 10 && g == 0) p -= BOT; else if (p == BOT && g <= 10 && b == 0) p += BOT; } else if (g == 0 && b == 0) p = DFL; else p = 0; return p; } static int insert(int *msgvec, enum entry entry, int incr) { int *ip; unsigned long u = 0; verbose = value("verbose") != NULL; if (getdb(O_RDWR) != OKAY) return 1; switch (entry) { case GOOD: u = getn(&super[OF_super_ngood]); break; case BAD: u = getn(&super[OF_super_nbad]); break; } for (ip = msgvec; *ip; ip++) { setdot(&message[*ip-1]); if (incr > 0 && u == MAX4-incr+1) { fprintf(stderr, "Junk mail database overflow.\n"); break; } else if (incr < 0 && -incr > u) { fprintf(stderr, "Junk mail database underflow.\n"); break; } u += incr; if (entry == GOOD && incr > 0 || entry == BAD && incr < 0) message[*ip-1].m_flag &= ~MJUNK; else message[*ip-1].m_flag |= MJUNK; scan(&message[*ip-1], entry, add, incr); } switch (entry) { case GOOD: putn(&super[OF_super_ngood], u); break; case BAD: putn(&super[OF_super_nbad], u); break; } if (table_version < 1) recompute(); putdb(); relsedb(); return 0; } int cgood(void *v) { return insert(v, GOOD, 1); } int cjunk(void *v) { return insert(v, BAD, 1); } int cungood(void *v) { return insert(v, GOOD, -1); } int cunjunk(void *v) { return insert(v, BAD, -1); } int cclassify(void *v) { int *msgvec = v, *ip; verbose = value("verbose") != NULL; _debug = debug || value("debug") != NULL; if (getdb(O_RDONLY) != OKAY) return 1; for (ip = msgvec; *ip; ip++) { setdot(&message[*ip-1]); clsf(&message[*ip-1]); } relsedb(); return 0; } #define BEST 15 static struct { float dist; float prob; char *word; unsigned long hash1; unsigned long hash2; enum loc loc; } best[BEST]; static void clsf(struct message *m) { int i; float a = 1, b = 1, r; if (verbose) fprintf(stderr, "Examining message %d\n", m - &message[0] + 1); for (i = 0; i < BEST; i++) { best[i].dist = 0; best[i].prob = -1; } if (scan(m, -1, rate, 0) != OKAY) return; if (best[0].prob == -1) { if (verbose) fprintf(stderr, "No information found.\n"); m->m_flag &= ~MJUNK; return; } for (i = 0; i < BEST; i++) { if (best[i].prob == -1) break; if (verbose) fprintf(stderr, "Probe %2d: \"%s\", hash=%lu:%lu " "prob=%.4g dist=%.4g\n", i+1, prstr(best[i].word), best[i].hash1, best[i].hash2, best[i].prob, best[i].dist); a *= best[i].prob; b *= 1 - best[i].prob; } r = a+b > 0 ? a / (a+b) : 0; if (verbose) fprintf(stderr, "Junk probability of message %d: %g\n", m - &message[0] + 1, r); if (r > THR) m->m_flag |= MJUNK; else m->m_flag &= ~MJUNK; } /*ARGSUSED4*/ static void rate(const char *word, enum entry entry, struct lexstat *sp, int unused) { char *n; unsigned long h1, h2; float p, d; int i, j; dbhash(word, &h1, &h2); if ((n = lookup(h1, h2, 0)) != NULL) { p = getprob(n); } else p = DFL; if (_debug) fprintf(stderr, "h=%lu:%lu g=%u b=%u p=%.4g %s\n", h1, h2, n ? get(&n[OF_node_good]) : 0, n ? get(&n[OF_node_bad]) : 0, p, prstr(word)); if (p == 0) return; d = p >= MID ? p - MID : MID - p; if (d >= best[BEST-1].dist) for (i = 0; i < BEST; i++) { if (h1 == best[i].hash1 && h2 == best[i].hash2) break; /* * For equal distance, this selection prefers * words with a low probability, since a false * negative is better than a false positive, * and since experience has shown that false * positives are more likely otherwise. Then, * words from the end of the header and from * the start of the body are preferred. This * gives the most interesting verbose output. */ if (d > best[i].dist || d == best[i].dist && p < best[i].prob || best[i].loc == HEADER && d == best[i].dist) { for (j = BEST-2; j >= i; j--) best[j+1] = best[j]; best[i].dist = d; best[i].prob = p; best[i].word = savestr(word); best[i].hash1 = h1; best[i].hash2 = h2; best[i].loc = sp->loc; break; } } } static void dbhash(const char *word, unsigned long *h1, unsigned long *h2) { unsigned char digest[16]; MD5_CTX ctx; MD5Init(&ctx); MD5Update(&ctx, (unsigned char *)word, strlen(word)); if (table_version >= 1) MD5Update(&ctx, (unsigned char *)&super[OF_super_mangle], 4); MD5Final(digest, &ctx); *h1 = getn(digest); if (table_version < 1) { *h1 ^= getn(&super[OF_super_mangle]); *h2 = 0; } else *h2 = get(&digest[4]); } /* * The selection of the value for mangling is not critical. It is practically * impossible for any person to determine the exact time when the database * was created first (without looking at the database, which would reveal the * value anyway), so we just use this. The MD5 hash here ensures that each * single second gives a completely different mangling value (which is not * necessary anymore if table_version>=1, but does not hurt). */ static void mkmangle(void) { union { time_t t; char c[16]; } u; unsigned long s; unsigned char digest[16]; MD5_CTX ctx; memset(&u, 0, sizeof u); time(&u.t); MD5Init(&ctx); MD5Update(&ctx, (unsigned char *)u.c, sizeof u.c); MD5Final(digest, &ctx); s = getn(digest); putn(&super[OF_super_mangle], s); } int cprobability(void *v) { char **args = v; unsigned long used, ngood, nbad; unsigned long h1, h2; unsigned g, b; float p, d; char *n; if (*args == NULL) { fprintf(stderr, "No words given.\n"); return 1; } if (getdb(O_RDONLY) != OKAY) return 1; used = getn(&super[OF_super_used]); ngood = getn(&super[OF_super_ngood]); nbad = getn(&super[OF_super_nbad]); printf("Database statistics: tokens=%lu ngood=%lu nbad=%lu\n", used, ngood, nbad); do { dbhash(*args, &h1, &h2); printf("\"%s\", hash=%lu:%lu ", *args, h1, h2); if ((n = lookup(h1, h2, 0)) != NULL) { g = get(&n[OF_node_good]); b = get(&n[OF_node_bad]); printf("good=%u bad=%u ", g, b); p = getprob(n); if (p != 0) { d = p >= MID ? p - MID : MID - p; printf("prob=%.4g dist=%.4g", p, d); } else printf("too infrequent"); } else printf("not in database"); putchar('\n'); } while (*++args); relsedb(); return 0; } heirloom-mailx-12.5/lex.c000066400000000000000000000534671155563371200153520ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)lex.c 2.86 (gritter) 12/25/06"; #endif #endif /* not lint */ #include "rcv.h" #include "extern.h" #include #include #include #include /* * Mail -- a mail program * * Lexical processing of commands. */ static char *prompt; static sighandler_type oldpipe; static const struct cmd *lex(char *Word); static void stop(int s); static void hangup(int s); /* * Set up editing on the given file name. * If the first character of name is %, we are considered to be * editing the file, otherwise we are reading our mail which has * signficance for mbox and so forth. * * newmail: Check for new mail in the current folder only. */ int setfile(char *name, int newmail) { FILE *ibuf; int i, compressed = 0; struct stat stb; char isedit; char *who = name[1] ? name + 1 : myname; static int shudclob; size_t offset; int omsgCount = 0; struct shortcut *sh; struct flock flp; isedit = *name != '%' && ((sh = get_shortcut(name)) == NULL || *sh->sh_long != '%'); if ((name = expand(name)) == NULL) return -1; switch (which_protocol(name)) { case PROTO_FILE: break; case PROTO_MAILDIR: return maildir_setfile(name, newmail, isedit); case PROTO_POP3: shudclob = 1; return pop3_setfile(name, newmail, isedit); case PROTO_IMAP: shudclob = 1; if (newmail) { if (mb.mb_type == MB_CACHE) return 1; omsgCount = msgCount; } return imap_setfile(name, newmail, isedit); case PROTO_UNKNOWN: fprintf(stderr, catgets(catd, CATSET, 217, "Cannot handle protocol: %s\n"), name); return -1; } if ((ibuf = Zopen(name, "r", &compressed)) == NULL) { if ((!isedit && errno == ENOENT) || newmail) { if (newmail) goto nonewmail; goto nomail; } perror(name); return(-1); } if (fstat(fileno(ibuf), &stb) < 0) { Fclose(ibuf); if (newmail) goto nonewmail; perror("fstat"); return (-1); } if (S_ISDIR(stb.st_mode)) { Fclose(ibuf); if (newmail) goto nonewmail; errno = EISDIR; perror(name); return (-1); } else if (S_ISREG(stb.st_mode)) { /*EMPTY*/ } else { Fclose(ibuf); if (newmail) goto nonewmail; errno = EINVAL; perror(name); return (-1); } /* * Looks like all will be well. We must now relinquish our * hold on the current set of stuff. Must hold signals * while we are reading the new file, else we will ruin * the message[] data structure. */ holdsigs(); if (shudclob && !newmail) quit(); #ifdef HAVE_SOCKETS if (!newmail && mb.mb_sock.s_fd >= 0) sclose(&mb.mb_sock); #endif /* HAVE_SOCKETS */ /* * Copy the messages into /tmp * and set pointers. */ flp.l_type = F_RDLCK; flp.l_start = 0; flp.l_whence = SEEK_SET; if (!newmail) { mb.mb_type = MB_FILE; mb.mb_perm = Rflag ? 0 : MB_DELE|MB_EDIT; mb.mb_compressed = compressed; if (compressed) { if (compressed & 0200) mb.mb_perm = 0; } else { if ((i = open(name, O_WRONLY)) < 0) mb.mb_perm = 0; else close(i); } if (shudclob) { if (mb.mb_itf) { fclose(mb.mb_itf); mb.mb_itf = NULL; } if (mb.mb_otf) { fclose(mb.mb_otf); mb.mb_otf = NULL; } } shudclob = 1; edit = isedit; initbox(name); offset = 0; flp.l_len = 0; if (!edit && fcntl(fileno(ibuf), F_SETLKW, &flp) < 0) { perror("Unable to lock mailbox"); Fclose(ibuf); return -1; } } else /* newmail */{ fseek(mb.mb_otf, 0L, SEEK_END); fseek(ibuf, mailsize, SEEK_SET); offset = mailsize; omsgCount = msgCount; flp.l_len = offset; if (!edit && fcntl(fileno(ibuf), F_SETLKW, &flp) < 0) goto nonewmail; } mailsize = fsize(ibuf); if (newmail && mailsize <= offset) { relsesigs(); goto nonewmail; } setptr(ibuf, offset); setmsize(msgCount); if (newmail && mb.mb_sorted) { mb.mb_threaded = 0; sort((void *)-1); } Fclose(ibuf); relsesigs(); if (!newmail) sawcom = 0; if ((!edit || newmail) && msgCount == 0) { nonewmail: if (!newmail) { if (value("emptystart") == NULL) nomail: fprintf(stderr, catgets(catd, CATSET, 88, "No mail for %s\n"), who); } return 1; } if (newmail) { newmailinfo(omsgCount); } return(0); } int newmailinfo(int omsgCount) { int mdot; int i; for (i = 0; i < omsgCount; i++) message[i].m_flag &= ~MNEWEST; if (msgCount > omsgCount) { for (i = omsgCount; i < msgCount; i++) message[i].m_flag |= MNEWEST; printf(catgets(catd, CATSET, 158, "New mail has arrived.\n")); if (msgCount - omsgCount == 1) printf(catgets(catd, CATSET, 214, "Loaded 1 new message\n")); else printf(catgets(catd, CATSET, 215, "Loaded %d new messages\n"), msgCount - omsgCount); } else printf("Loaded %d messages\n", msgCount); callhook(mailname, 1); mdot = getmdot(1); if (value("header")) { if (mb.mb_type == MB_IMAP) imap_getheaders(omsgCount+1, msgCount); while (++omsgCount <= msgCount) if (visible(&message[omsgCount-1])) printhead(omsgCount, stdout, 0); } return mdot; } static int *msgvec; static int reset_on_stop; /* do a reset() if stopped */ /* * Interpret user commands one by one. If standard input is not a tty, * print no prompt. */ void commands(void) { int eofloop = 0; int n, x; char *linebuf = NULL, *av, *nv; size_t linesize = 0; (void)&eofloop; if (!sourcing) { if (safe_signal(SIGINT, SIG_IGN) != SIG_IGN) safe_signal(SIGINT, onintr); if (safe_signal(SIGHUP, SIG_IGN) != SIG_IGN) safe_signal(SIGHUP, hangup); safe_signal(SIGTSTP, stop); safe_signal(SIGTTOU, stop); safe_signal(SIGTTIN, stop); } oldpipe = safe_signal(SIGPIPE, SIG_IGN); safe_signal(SIGPIPE, oldpipe); setexit(); for (;;) { interrupts = 0; handlerstacktop = NULL; /* * Print the prompt, if needed. Clear out * string space, and flush the output. */ if (!sourcing && value("interactive") != NULL) { av = (av = value("autoinc")) ? savestr(av) : NULL; nv = (nv = value("newmail")) ? savestr(nv) : NULL; if (is_a_tty[0] && (av != NULL || nv != NULL || mb.mb_type == MB_IMAP)) { struct stat st; n = (av && strcmp(av, "noimap") && strcmp(av, "nopoll")) | (nv && strcmp(nv, "noimap") && strcmp(nv, "nopoll")); x = !(av || nv); if ((mb.mb_type == MB_FILE && stat(mailname, &st) == 0 && st.st_size > mailsize) || (mb.mb_type == MB_IMAP && imap_newmail(n) > x) || (mb.mb_type == MB_MAILDIR && n != 0)) { int odot = dot - &message[0]; int odid = did_print_dot; setfile(mailname, 1); if (mb.mb_type != MB_IMAP) { dot = &message[odot]; did_print_dot = odid; } } } reset_on_stop = 1; if ((prompt = value("prompt")) == NULL) prompt = value("bsdcompat") ? "& " : "? "; printf("%s", prompt); } fflush(stdout); sreset(); /* * Read a line of commands from the current input * and handle end of file specially. */ n = 0; for (;;) { n = readline_restart(input, &linebuf, &linesize, n); if (n < 0) break; if (n == 0 || linebuf[n - 1] != '\\') break; linebuf[n - 1] = ' '; } reset_on_stop = 0; if (n < 0) { /* eof */ if (loading) break; if (sourcing) { unstack(); continue; } if (value("interactive") != NULL && value("ignoreeof") != NULL && ++eofloop < 25) { printf(catgets(catd, CATSET, 89, "Use \"quit\" to quit.\n")); continue; } break; } eofloop = 0; inhook = 0; if (execute(linebuf, 0, n)) break; } if (linebuf) free(linebuf); } /* * Execute a single command. * Command functions return 0 for success, 1 for error, and -1 * for abort. A 1 or -1 aborts a load or source. A -1 aborts * the interactive command loop. * Contxt is non-zero if called while composing mail. */ int execute(char *linebuf, int contxt, size_t linesize) { char *word; char *arglist[MAXARGC]; const struct cmd *com = (struct cmd *)NULL; char *cp, *cp2; int c; int muvec[2]; int e = 1; /* * Strip the white space away from the beginning * of the command, then scan out a word, which * consists of anything except digits and white space. * * Handle ! escapes differently to get the correct * lexical conventions. */ word = ac_alloc(linesize + 1); for (cp = linebuf; whitechar(*cp & 0377); cp++); if (*cp == '!') { if (sourcing) { printf(catgets(catd, CATSET, 90, "Can't \"!\" while sourcing\n")); goto out; } shell(cp+1); ac_free(word); return(0); } if (*cp == '#') { ac_free(word); return 0; } cp2 = word; if (*cp != '|') { while (*cp && strchr(" \t0123456789$^.:/-+*'\",;(`", *cp) == NULL) *cp2++ = *cp++; } else *cp2++ = *cp++; *cp2 = '\0'; /* * Look up the command; if not found, bitch. * Normally, a blank command would map to the * first command in the table; while sourcing, * however, we ignore blank lines to eliminate * confusion. */ if (sourcing && *word == '\0') { ac_free(word); return(0); } com = lex(word); if (com == NULL) { printf(catgets(catd, CATSET, 91, "Unknown command: \"%s\"\n"), word); goto out; } /* * See if we should execute the command -- if a conditional * we always execute it, otherwise, check the state of cond. */ if ((com->c_argtype & F) == 0) { if ((cond == CRCV && !rcvmode) || (cond == CSEND && rcvmode) || (cond == CTERM && !is_a_tty[0]) || (cond == CNONTERM && is_a_tty[0])) { ac_free(word); return(0); } } /* * Process the arguments to the command, depending * on the type he expects. Default to an error. * If we are sourcing an interactive command, it's * an error. */ if (!rcvmode && (com->c_argtype & M) == 0) { printf(catgets(catd, CATSET, 92, "May not execute \"%s\" while sending\n"), com->c_name); goto out; } if (sourcing && com->c_argtype & I) { printf(catgets(catd, CATSET, 93, "May not execute \"%s\" while sourcing\n"), com->c_name); goto out; } if ((mb.mb_perm & MB_DELE) == 0 && com->c_argtype & W) { printf(catgets(catd, CATSET, 94, "May not execute \"%s\" -- message file is read only\n"), com->c_name); goto out; } if (contxt && com->c_argtype & R) { printf(catgets(catd, CATSET, 95, "Cannot recursively invoke \"%s\"\n"), com->c_name); goto out; } if (mb.mb_type == MB_VOID && com->c_argtype & A) { printf(catgets(catd, CATSET, 257, "Cannot execute \"%s\" without active mailbox\n"), com->c_name); goto out; } switch (com->c_argtype & ~(F|P|I|M|T|W|R|A)) { case MSGLIST: /* * A message list defaulting to nearest forward * legal message. */ if (msgvec == 0) { printf(catgets(catd, CATSET, 96, "Illegal use of \"message list\"\n")); break; } if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0) break; if (c == 0) { if ((*msgvec = first(com->c_msgflag, com->c_msgmask)) != 0) msgvec[1] = 0; } if (*msgvec == 0) { if (!inhook) printf(catgets(catd, CATSET, 97, "No applicable messages\n")); break; } e = (*com->c_func)(msgvec); break; case NDMLIST: /* * A message list with no defaults, but no error * if none exist. */ if (msgvec == 0) { printf(catgets(catd, CATSET, 98, "Illegal use of \"message list\"\n")); break; } if (getmsglist(cp, msgvec, com->c_msgflag) < 0) break; e = (*com->c_func)(msgvec); break; case STRLIST: /* * Just the straight string, with * leading blanks removed. */ while (whitechar(*cp & 0377)) cp++; e = (*com->c_func)(cp); break; case RAWLIST: case ECHOLIST: /* * A vector of strings, in shell style. */ if ((c = getrawlist(cp, linesize, arglist, sizeof arglist / sizeof *arglist, (com->c_argtype&~(F|P|I|M|T|W|R|A))==ECHOLIST)) < 0) break; if (c < com->c_minargs) { printf(catgets(catd, CATSET, 99, "%s requires at least %d arg(s)\n"), com->c_name, com->c_minargs); break; } if (c > com->c_maxargs) { printf(catgets(catd, CATSET, 100, "%s takes no more than %d arg(s)\n"), com->c_name, com->c_maxargs); break; } e = (*com->c_func)(arglist); break; case NOLIST: /* * Just the constant zero, for exiting, * eg. */ e = (*com->c_func)(0); break; default: panic(catgets(catd, CATSET, 101, "Unknown argtype")); } out: ac_free(word); /* * Exit the current source file on * error. */ if (e) { if (e < 0) return 1; if (loading) return 1; if (sourcing) unstack(); return 0; } if (com == (struct cmd *)NULL) return(0); if (value("autoprint") != NULL && com->c_argtype & P) if (visible(dot)) { muvec[0] = dot - &message[0] + 1; muvec[1] = 0; type(muvec); } if (!sourcing && !inhook && (com->c_argtype & T) == 0) sawcom = 1; return(0); } /* * Set the size of the message vector used to construct argument * lists to message list functions. */ void setmsize(int sz) { if (msgvec != 0) free(msgvec); msgvec = (int *)scalloc((sz + 1), sizeof *msgvec); } /* * Find the correct command in the command table corresponding * to the passed command "word" */ static const struct cmd * lex(char *Word) { extern const struct cmd cmdtab[]; const struct cmd *cp; for (cp = &cmdtab[0]; cp->c_name != NULL; cp++) if (is_prefix(Word, cp->c_name)) return(cp); return(NULL); } /* * The following gets called on receipt of an interrupt. This is * to abort printout of a command, mainly. * Dispatching here when command() is inactive crashes rcv. * Close all open files except 0, 1, 2, and the temporary. * Also, unstack all source files. */ static int inithdr; /* am printing startup headers */ /*ARGSUSED*/ void onintr(int s) { if (handlerstacktop != NULL) { handlerstacktop(s); return; } safe_signal(SIGINT, onintr); noreset = 0; if (!inithdr) sawcom++; inithdr = 0; while (sourcing) unstack(); close_all_files(); if (image >= 0) { close(image); image = -1; } if (interrupts != 1) fprintf(stderr, catgets(catd, CATSET, 102, "Interrupt\n")); safe_signal(SIGPIPE, oldpipe); reset(0); } /* * When we wake up after ^Z, reprint the prompt. */ static void stop(int s) { sighandler_type old_action = safe_signal(s, SIG_DFL); sigset_t nset; sigemptyset(&nset); sigaddset(&nset, s); sigprocmask(SIG_UNBLOCK, &nset, (sigset_t *)NULL); kill(0, s); sigprocmask(SIG_BLOCK, &nset, (sigset_t *)NULL); safe_signal(s, old_action); if (reset_on_stop) { reset_on_stop = 0; reset(0); } } /* * Branch here on hangup signal and simulate "exit". */ /*ARGSUSED*/ static void hangup(int s) { /* nothing to do? */ exit(1); } /* * Announce the presence of the current Mail version, * give the message count, and print a header listing. */ void announce(int printheaders) { int vec[2], mdot; mdot = newfileinfo(); vec[0] = mdot; vec[1] = 0; dot = &message[mdot - 1]; if (printheaders && msgCount > 0 && value("header") != NULL) { inithdr++; headers(vec); inithdr = 0; } } /* * Announce information about the file we are editing. * Return a likely place to set dot. */ int newfileinfo(void) { struct message *mp; int u, n, mdot, d, s, hidden, killed, moved; char fname[PATHSIZE], zname[PATHSIZE], *ename; if (mb.mb_type == MB_VOID) return 1; mdot = getmdot(0); s = d = hidden = killed = moved =0; for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) { if (mp->m_flag & MNEW) n++; if ((mp->m_flag & MREAD) == 0) u++; if ((mp->m_flag & (MDELETED|MSAVED)) == (MDELETED|MSAVED)) moved++; if ((mp->m_flag & (MDELETED|MSAVED)) == MDELETED) d++; if ((mp->m_flag & (MDELETED|MSAVED)) == MSAVED) s++; if (mp->m_flag & MHIDDEN) hidden++; if (mp->m_flag & MKILL) killed++; } ename = mailname; if (getfold(fname, sizeof fname - 1) >= 0) { strcat(fname, "/"); if (which_protocol(fname) != PROTO_IMAP && strncmp(fname, mailname, strlen(fname)) == 0) { snprintf(zname, sizeof zname, "+%s", mailname + strlen(fname)); ename = zname; } } printf(catgets(catd, CATSET, 103, "\"%s\": "), ename); if (msgCount == 1) printf(catgets(catd, CATSET, 104, "1 message")); else printf(catgets(catd, CATSET, 105, "%d messages"), msgCount); if (n > 0) printf(catgets(catd, CATSET, 106, " %d new"), n); if (u-n > 0) printf(catgets(catd, CATSET, 107, " %d unread"), u); if (d > 0) printf(catgets(catd, CATSET, 108, " %d deleted"), d); if (s > 0) printf(catgets(catd, CATSET, 109, " %d saved"), s); if (moved > 0) printf(catgets(catd, CATSET, 109, " %d moved"), moved); if (hidden > 0) printf(catgets(catd, CATSET, 109, " %d hidden"), hidden); if (killed > 0) printf(catgets(catd, CATSET, 109, " %d killed"), killed); if (mb.mb_type == MB_CACHE) printf(" [Disconnected]"); else if (mb.mb_perm == 0) printf(catgets(catd, CATSET, 110, " [Read only]")); printf("\n"); return(mdot); } int getmdot(int newmail) { struct message *mp; char *cp; int mdot; enum mflag avoid = MHIDDEN|MKILL|MDELETED; if (!newmail) { if (value("autothread")) thread(NULL); else if ((cp = value("autosort")) != NULL) { free(mb.mb_sorted); mb.mb_sorted = sstrdup(cp); sort(NULL); } } if (mb.mb_type == MB_VOID) return 1; if (newmail) for (mp = &message[0]; mp < &message[msgCount]; mp++) if ((mp->m_flag & (MNEWEST|avoid)) == MNEWEST) break; if (!newmail || mp >= &message[msgCount]) { for (mp = mb.mb_threaded ? threadroot : &message[0]; mb.mb_threaded ? mp != NULL : mp < &message[msgCount]; mb.mb_threaded ? mp = next_in_thread(mp) : mp++) if ((mp->m_flag & (MNEW|avoid)) == MNEW) break; } if (mb.mb_threaded ? mp == NULL : mp >= &message[msgCount]) for (mp = mb.mb_threaded ? threadroot : &message[0]; mb.mb_threaded ? mp != NULL: mp < &message[msgCount]; mb.mb_threaded ? mp = next_in_thread(mp) : mp++) if (mp->m_flag & MFLAGGED) break; if (mb.mb_threaded ? mp == NULL : mp >= &message[msgCount]) for (mp = mb.mb_threaded ? threadroot : &message[0]; mb.mb_threaded ? mp != NULL: mp < &message[msgCount]; mb.mb_threaded ? mp = next_in_thread(mp) : mp++) if ((mp->m_flag & (MREAD|avoid)) == 0) break; if (mb.mb_threaded ? mp != NULL : mp < &message[msgCount]) mdot = mp - &message[0] + 1; else if (value("showlast")) { if (mb.mb_threaded) { for (mp = this_in_thread(threadroot, -1); mp; mp = prev_in_thread(mp)) if ((mp->m_flag & avoid) == 0) break; mdot = mp ? mp - &message[0] + 1 : msgCount; } else { for (mp = &message[msgCount-1]; mp >= &message[0]; mp--) if ((mp->m_flag & avoid) == 0) break; mdot = mp >= &message[0] ? mp-&message[0]+1 : msgCount; } } else if (mb.mb_threaded) { for (mp = threadroot; mp; mp = next_in_thread(mp)) if ((mp->m_flag & avoid) == 0) break; mdot = mp ? mp - &message[0] + 1 : 1; } else { for (mp = &message[0]; mp < &message[msgCount]; mp++) if ((mp->m_flag & avoid) == 0) break; mdot = mp < &message[msgCount] ? mp-&message[0]+1 : 1; } return mdot; } /* * Print the current version number. */ /*ARGSUSED*/ int pversion(void *v) { printf(catgets(catd, CATSET, 111, "Version %s\n"), version); return(0); } /* * Load a file of user definitions. */ void load(char *name) { FILE *in, *oldin; if ((in = Fopen(name, "r")) == NULL) return; oldin = input; input = in; loading = 1; sourcing = 1; commands(); loading = 0; sourcing = 0; input = oldin; Fclose(in); } void initbox(const char *name) { char *tempMesg; int dummy; if (mb.mb_type != MB_VOID) { strncpy(prevfile, mailname, PATHSIZE); prevfile[PATHSIZE-1]='\0'; } if (name != mailname) { strncpy(mailname, name, PATHSIZE); mailname[PATHSIZE-1]='\0'; } if ((mb.mb_otf = Ftemp(&tempMesg, "Rx", "w", 0600, 0)) == NULL) { perror(catgets(catd, CATSET, 87, "temporary mail message file")); exit(1); } fcntl(fileno(mb.mb_otf), F_SETFD, FD_CLOEXEC); if ((mb.mb_itf = safe_fopen(tempMesg, "r", &dummy)) == NULL) { perror(tempMesg); exit(1); } fcntl(fileno(mb.mb_itf), F_SETFD, FD_CLOEXEC); rm(tempMesg); Ftfree(&tempMesg); msgCount = 0; if (message) { free(message); message = NULL; msgspace = 0; } mb.mb_threaded = 0; free(mb.mb_sorted); mb.mb_sorted = NULL; mb.mb_flags = MB_NOFLAGS; prevdot = NULL; dot = NULL; did_print_dot = 0; } heirloom-mailx-12.5/list.c000066400000000000000000000630041155563371200155210ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)list.c 2.62 (gritter) 12/11/08"; #endif #endif /* not lint */ #include "rcv.h" #include #include "extern.h" #ifdef HAVE_WCTYPE_H #include #endif /* HAVE_WCTYPE_H */ /* * Mail -- a mail program * * Message list handling. */ enum idfield { ID_REFERENCES, ID_IN_REPLY_TO }; static char **add_to_namelist(char ***namelist, size_t *nmlsize, char **np, char *string); static int markall(char *buf, int f); static int evalcol(int col); static int check(int mesg, int f); static int scan(char **sp); static void regret(int token); static void scaninit(void); static int matchsender(char *str, int mesg, int allnet); static int matchmid(char *id, enum idfield idfield, int mesg); static int matchsubj(char *str, int mesg); static void unmark(int mesg); static int metamess(int meta, int f); static size_t STRINGLEN; static int lexnumber; /* Number of TNUMBER from scan() */ static char *lexstring; /* String from TSTRING, scan() */ static int regretp; /* Pointer to TOS of regret tokens */ static int regretstack[REGDEP]; /* Stack of regretted tokens */ static char *string_stack[REGDEP]; /* Stack of regretted strings */ static int numberstack[REGDEP]; /* Stack of regretted numbers */ static int threadflag; /* mark entire threads */ /* * Convert the user string of message numbers and * store the numbers into vector. * * Returns the count of messages picked up or -1 on error. */ int getmsglist(char *buf, int *vector, int flags) { int *ip; struct message *mp; int mc; if (msgCount == 0) { *vector = 0; return 0; } if (markall(buf, flags) < 0) return(-1); ip = vector; if (inhook & 2) { mc = 0; for (mp = &message[0]; mp < &message[msgCount]; mp++) if (mp->m_flag & MMARK) { if ((mp->m_flag & MNEWEST) == 0) unmark(mp - &message[0] + 1); else mc++; } if (mc == 0) return -1; } if (mb.mb_threaded == 0) { for (mp = &message[0]; mp < &message[msgCount]; mp++) if (mp->m_flag & MMARK) *ip++ = mp - &message[0] + 1; } else { for (mp = threadroot; mp; mp = next_in_thread(mp)) if (mp->m_flag & MMARK) *ip++ = mp - &message[0] + 1; } *ip = 0; return(ip - vector); } /* * Mark all messages that the user wanted from the command * line in the message structure. Return 0 on success, -1 * on error. */ /* * Bit values for colon modifiers. */ #define CMNEW 01 /* New messages */ #define CMOLD 02 /* Old messages */ #define CMUNREAD 04 /* Unread messages */ #define CMDELETED 010 /* Deleted messages */ #define CMREAD 020 /* Read messages */ #define CMFLAG 040 /* Flagged messages */ #define CMANSWER 0100 /* Answered messages */ #define CMDRAFT 0200 /* Draft messages */ #define CMKILL 0400 /* Killed messages */ #define CMJUNK 01000 /* Junk messages */ /* * The following table describes the letters which can follow * the colon and gives the corresponding modifier bit. */ static struct coltab { char co_char; /* What to find past : */ int co_bit; /* Associated modifier bit */ int co_mask; /* m_status bits to mask */ int co_equal; /* ... must equal this */ } coltab[] = { { 'n', CMNEW, MNEW, MNEW }, { 'o', CMOLD, MNEW, 0 }, { 'u', CMUNREAD, MREAD, 0 }, { 'd', CMDELETED, MDELETED, MDELETED }, { 'r', CMREAD, MREAD, MREAD }, { 'f', CMFLAG, MFLAGGED, MFLAGGED }, { 'a', CMANSWER, MANSWERED, MANSWERED }, { 't', CMDRAFT, MDRAFTED, MDRAFTED }, { 'k', CMKILL, MKILL, MKILL }, { 'j', CMJUNK, MJUNK, MJUNK }, { 0, 0, 0, 0 } }; static int lastcolmod; static char ** add_to_namelist(char ***namelist, size_t *nmlsize, char **np, char *string) { size_t idx; if ((idx = np - *namelist) >= *nmlsize) { *namelist = srealloc(*namelist, (*nmlsize += 8) * sizeof *np); np = &(*namelist)[idx]; } *np++ = string; return np; } #define markall_ret(i) { \ retval = i; \ ac_free(lexstring); \ goto out; \ } static int markall(char *buf, int f) { char **np, **nq; int i, retval, gotheaders; struct message *mp, *mx; char **namelist, *bufp, *id = NULL, *cp; int tok, beg, mc, star, other, valdot, colmod, colresult, topen, tback; size_t nmlsize; enum idfield idfield = ID_REFERENCES; lexstring = ac_alloc(STRINGLEN = 2 * strlen(buf) + 1); valdot = dot - &message[0] + 1; colmod = 0; for (i = 1; i <= msgCount; i++) { message[i-1].m_flag &= ~MOLDMARK; if (message[i-1].m_flag & MMARK) message[i-1].m_flag |= MOLDMARK; unmark(i); } bufp = buf; mc = 0; namelist = smalloc((nmlsize = 8) * sizeof *namelist); np = &namelist[0]; scaninit(); tok = scan(&bufp); star = 0; other = 0; beg = 0; topen = 0; tback = 0; gotheaders = 0; while (tok != TEOL) { switch (tok) { case TNUMBER: number: if (star) { printf(catgets(catd, CATSET, 112, "No numbers mixed with *\n")); markall_ret(-1) } mc++; other++; if (beg != 0) { if (check(lexnumber, f)) markall_ret(-1) i = beg; while (mb.mb_threaded ? 1 : i <= lexnumber) { if (!(message[i-1].m_flag&MHIDDEN) && (f == MDELETED || (message[i-1].m_flag & MDELETED) == 0)) mark(i, f); if (mb.mb_threaded) { if (i == lexnumber) break; mx = next_in_thread(&message[i-1]); if (mx == NULL) markall_ret(-1) i = mx-message+1; } else i++; } beg = 0; break; } beg = lexnumber; if (check(beg, f)) markall_ret(-1) tok = scan(&bufp); regret(tok); if (tok != TDASH) { mark(beg, f); beg = 0; } break; case TPLUS: if (beg != 0) { printf(catgets(catd, CATSET, 113, "Non-numeric second argument\n")); markall_ret(-1) } i = valdot; do { if (mb.mb_threaded) { mx = next_in_thread(&message[i-1]); i = mx ? mx-message+1 : msgCount+1; } else i++; if (i > msgCount) { printf(catgets(catd, CATSET, 114, "Referencing beyond EOF\n")); markall_ret(-1) } } while (message[i-1].m_flag == MHIDDEN || (message[i-1].m_flag & MDELETED) != f || message[i-1].m_flag & MKILL); mark(i, f); break; case TDASH: if (beg == 0) { i = valdot; do { if (mb.mb_threaded) { mx = prev_in_thread( &message[i-1]); i = mx ? mx-message+1 : 0; } else i--; if (i <= 0) { printf(catgets(catd, CATSET, 115, "Referencing before 1\n")); markall_ret(-1) } } while (message[i-1].m_flag & MHIDDEN || (message[i-1].m_flag & MDELETED) != f || message[i-1].m_flag & MKILL); mark(i, f); } break; case TSTRING: if (beg != 0) { printf(catgets(catd, CATSET, 116, "Non-numeric second argument\n")); markall_ret(-1) } other++; if (lexstring[0] == ':') { colresult = evalcol(lexstring[1]); if (colresult == 0) { printf(catgets(catd, CATSET, 117, "Unknown colon modifier \"%s\"\n"), lexstring); markall_ret(-1) } colmod |= colresult; } else np = add_to_namelist(&namelist, &nmlsize, np, savestr(lexstring)); break; case TOPEN: if (imap_search(lexstring, f) == STOP) markall_ret(-1) topen++; break; case TDOLLAR: case TUP: case TDOT: case TSEMI: lexnumber = metamess(lexstring[0], f); if (lexnumber == -1) markall_ret(-1) goto number; case TBACK: tback = 1; for (i = 1; i <= msgCount; i++) { if (message[i-1].m_flag&MHIDDEN || (message[i-1].m_flag&MDELETED) != f) continue; if (message[i-1].m_flag&MOLDMARK) mark(i, f); } break; case TSTAR: if (other) { printf(catgets(catd, CATSET, 118, "Can't mix \"*\" with anything\n")); markall_ret(-1) } star++; break; case TCOMMA: if (mb.mb_type == MB_IMAP && gotheaders++ == 0) imap_getheaders(1, msgCount); if (id == NULL && (cp = hfield("in-reply-to", dot)) != NULL) { id = savestr(cp); idfield = ID_IN_REPLY_TO; } if (id == NULL && (cp = hfield("references", dot)) != NULL) { struct name *np; if ((np = extract(cp, GREF)) != NULL) { while (np->n_flink != NULL) np = np->n_flink; id = savestr(np->n_name); idfield = ID_REFERENCES; } } if (id == NULL) { printf(catgets(catd, CATSET, 227, "Cannot determine parent Message-ID of the current message\n")); markall_ret(-1) } break; case TERROR: markall_ret(-1) } threadflag = 0; tok = scan(&bufp); } lastcolmod = colmod; np = add_to_namelist(&namelist, &nmlsize, np, NULL); np--; mc = 0; if (star) { for (i = 0; i < msgCount; i++) { if (!(message[i].m_flag & MHIDDEN) && (message[i].m_flag & MDELETED) == f) { mark(i+1, f); mc++; } } if (mc == 0) { if (!inhook) printf(catgets(catd, CATSET, 119, "No applicable messages.\n")); markall_ret(-1) } markall_ret(0) } if ((topen || tback) && mc == 0) { for (i = 0; i < msgCount; i++) if (message[i].m_flag & MMARK) mc++; if (mc == 0) { if (!inhook) printf(tback ? "No previously marked messages.\n" : "No messages satisfy (criteria).\n"); markall_ret(-1) } } /* * If no numbers were given, mark all of the messages, * so that we can unmark any whose sender was not selected * if any user names were given. */ if ((np > namelist || colmod != 0 || id) && mc == 0) for (i = 1; i <= msgCount; i++) { if (!(message[i-1].m_flag & MHIDDEN) && (message[i-1].m_flag & MDELETED) == f) mark(i, f); } /* * If any names were given, go through and eliminate any * messages whose senders were not requested. */ if (np > namelist || id) { int allnet = value("allnet") != NULL; if (mb.mb_type == MB_IMAP && gotheaders++ == 0) imap_getheaders(1, msgCount); for (i = 1; i <= msgCount; i++) { mc = 0; if (np > namelist) { for (nq = &namelist[0]; *nq != NULL; nq++) { if (**nq == '/') { if (matchsubj(*nq, i)) { mc++; break; } } else { if (matchsender(*nq, i, allnet)) { mc++; break; } } } } if (mc == 0 && id && matchmid(id, idfield, i)) mc++; if (mc == 0) unmark(i); } /* * Make sure we got some decent messages. */ mc = 0; for (i = 1; i <= msgCount; i++) if (message[i-1].m_flag & MMARK) { mc++; break; } if (mc == 0) { if (!inhook && np > namelist) { printf(catgets(catd, CATSET, 120, "No applicable messages from {%s"), namelist[0]); for (nq = &namelist[1]; *nq != NULL; nq++) printf(catgets(catd, CATSET, 121, ", %s"), *nq); printf(catgets(catd, CATSET, 122, "}\n")); } else if (id) { printf(catgets(catd, CATSET, 227, "Parent message not found\n")); } markall_ret(-1) } } /* * If any colon modifiers were given, go through and * unmark any messages which do not satisfy the modifiers. */ if (colmod != 0) { for (i = 1; i <= msgCount; i++) { struct coltab *colp; mp = &message[i - 1]; for (colp = &coltab[0]; colp->co_char; colp++) if (colp->co_bit & colmod) if ((mp->m_flag & colp->co_mask) != colp->co_equal) unmark(i); } for (mp = &message[0]; mp < &message[msgCount]; mp++) if (mp->m_flag & MMARK) break; if (mp >= &message[msgCount]) { struct coltab *colp; if (!inhook) { printf(catgets(catd, CATSET, 123, "No messages satisfy")); for (colp = &coltab[0]; colp->co_char; colp++) if (colp->co_bit & colmod) printf(" :%c", colp->co_char); printf("\n"); } markall_ret(-1) } } markall_ret(0) out: free(namelist); return retval; } /* * Turn the character after a colon modifier into a bit * value. */ static int evalcol(int col) { struct coltab *colp; if (col == 0) return(lastcolmod); for (colp = &coltab[0]; colp->co_char; colp++) if (colp->co_char == col) return(colp->co_bit); return(0); } /* * Check the passed message number for legality and proper flags. * If f is MDELETED, then either kind will do. Otherwise, the message * has to be undeleted. */ static int check(int mesg, int f) { struct message *mp; if (mesg < 1 || mesg > msgCount) { printf(catgets(catd, CATSET, 124, "%d: Invalid message number\n"), mesg); return(-1); } mp = &message[mesg-1]; if (mp->m_flag & MHIDDEN || (f != MDELETED && (mp->m_flag & MDELETED) != 0)) { printf(catgets(catd, CATSET, 125, "%d: Inappropriate message\n"), mesg); return(-1); } return(0); } /* * Scan out the list of string arguments, shell style * for a RAWLIST. */ int getrawlist(const char *line, size_t linesize, char **argv, int argc, int echolist) { char c, *cp2, quotec; const char *cp; int argn; char *linebuf; argn = 0; cp = line; linebuf = ac_alloc(linesize + 1); for (;;) { for (; blankchar(*cp & 0377); cp++); if (*cp == '\0') break; if (argn >= argc - 1) { printf(catgets(catd, CATSET, 126, "Too many elements in the list; excess discarded.\n")); break; } cp2 = linebuf; quotec = '\0'; while ((c = *cp) != '\0') { cp++; if (quotec != '\0') { if (c == quotec) { quotec = '\0'; if (echolist) *cp2++ = c; } else if (c == '\\') switch (c = *cp++) { case '\0': *cp2++ = '\\'; cp--; break; /* case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': c -= '0'; if (*cp >= '0' && *cp <= '7') c = c * 8 + *cp++ - '0'; if (*cp >= '0' && *cp <= '7') c = c * 8 + *cp++ - '0'; *cp2++ = c; break; case 'b': *cp2++ = '\b'; break; case 'f': *cp2++ = '\f'; break; case 'n': *cp2++ = '\n'; break; case 'r': *cp2++ = '\r'; break; case 't': *cp2++ = '\t'; break; case 'v': *cp2++ = '\v'; break; */ default: if (cp[-1]!=quotec || echolist) *cp2++ = '\\'; *cp2++ = c; } /*else if (c == '^') { c = *cp++; if (c == '?') *cp2++ = '\177'; /\* null doesn't show up anyway *\/ else if ((c >= 'A' && c <= '_') || (c >= 'a' && c <= 'z')) *cp2++ = c & 037; else { *cp2++ = '^'; cp--; } }*/ else *cp2++ = c; } else if (c == '"' || c == '\'') { if (echolist) *cp2++ = c; quotec = c; } else if (c == '\\' && !echolist) { if (*cp) *cp2++ = *cp++; else *cp2++ = c; } else if (blankchar(c & 0377)) break; else *cp2++ = c; } *cp2 = '\0'; argv[argn++] = savestr(linebuf); } argv[argn] = NULL; ac_free(linebuf); return argn; } /* * scan out a single lexical item and return its token number, * updating the string pointer passed **p. Also, store the value * of the number or string scanned in lexnumber or lexstring as * appropriate. In any event, store the scanned `thing' in lexstring. */ static struct lex { char l_char; enum ltoken l_token; } singles[] = { { '$', TDOLLAR }, { '.', TDOT }, { '^', TUP }, { '*', TSTAR }, { '-', TDASH }, { '+', TPLUS }, { '(', TOPEN }, { ')', TCLOSE }, { ',', TCOMMA }, { ';', TSEMI }, { '`', TBACK }, { 0, 0 } }; static int scan(char **sp) { char *cp, *cp2; int c, level, inquote; struct lex *lp; int quotec; if (regretp >= 0) { strncpy(lexstring, string_stack[regretp], STRINGLEN); lexstring[STRINGLEN-1]='\0'; lexnumber = numberstack[regretp]; return(regretstack[regretp--]); } cp = *sp; cp2 = lexstring; c = *cp++; /* * strip away leading white space. */ while (blankchar(c)) c = *cp++; /* * If no characters remain, we are at end of line, * so report that. */ if (c == '\0') { *sp = --cp; return(TEOL); } /* * Select members of a message thread. */ if (c == '&') { threadflag = 1; if (*cp == '\0' || spacechar(*cp&0377)) { lexstring[0] = '.'; lexstring[1] = '\0'; *sp = cp; return TDOT; } c = *cp++; } /* * If the leading character is a digit, scan * the number and convert it on the fly. * Return TNUMBER when done. */ if (digitchar(c)) { lexnumber = 0; while (digitchar(c)) { lexnumber = lexnumber*10 + c - '0'; *cp2++ = c; c = *cp++; } *cp2 = '\0'; *sp = --cp; return(TNUMBER); } /* * An IMAP SEARCH list. Note that TOPEN has always been included * in singles[] in Mail and mailx. Thus although there is no formal * definition for (LIST) lists, they do not collide with historical * practice because a subject string (LIST) could never been matched * this way. */ if (c == '(') { level = 1; inquote = 0; *cp2++ = c; do { if ((c = *cp++&0377) == '\0') { mtop: fprintf(stderr, "Missing \")\".\n"); return TERROR; } if (inquote && c == '\\') { *cp2++ = c; c = *cp++&0377; if (c == '\0') goto mtop; } else if (c == '"') inquote = !inquote; else if (inquote) /*EMPTY*/; else if (c == '(') level++; else if (c == ')') level--; else if (spacechar(c)) { /* * Replace unquoted whitespace by single * space characters, to make the string * IMAP SEARCH conformant. */ c = ' '; if (cp2[-1] == ' ') cp2--; } *cp2++ = c; } while (c != ')' || level > 0); *cp2 = '\0'; *sp = cp; return TOPEN; } /* * Check for single character tokens; return such * if found. */ for (lp = &singles[0]; lp->l_char != 0; lp++) if (c == lp->l_char) { lexstring[0] = c; lexstring[1] = '\0'; *sp = cp; return(lp->l_token); } /* * We've got a string! Copy all the characters * of the string into lexstring, until we see * a null, space, or tab. * If the lead character is a " or ', save it * and scan until you get another. */ quotec = 0; if (c == '\'' || c == '"') { quotec = c; c = *cp++; } while (c != '\0') { if (quotec == 0 && c == '\\' && *cp) c = *cp++; if (c == quotec) { cp++; break; } if (quotec == 0 && blankchar(c)) break; if (cp2 - lexstring < STRINGLEN-1) *cp2++ = c; c = *cp++; } if (quotec && c == 0) { fprintf(stderr, catgets(catd, CATSET, 127, "Missing %c\n"), quotec); return TERROR; } *sp = --cp; *cp2 = '\0'; return(TSTRING); } /* * Unscan the named token by pushing it onto the regret stack. */ static void regret(int token) { if (++regretp >= REGDEP) panic(catgets(catd, CATSET, 128, "Too many regrets")); regretstack[regretp] = token; lexstring[STRINGLEN-1] = '\0'; string_stack[regretp] = savestr(lexstring); numberstack[regretp] = lexnumber; } /* * Reset all the scanner global variables. */ static void scaninit(void) { regretp = -1; threadflag = 0; } /* * Find the first message whose flags & m == f and return * its message number. */ int first(int f, int m) { struct message *mp; if (msgCount == 0) return 0; f &= MDELETED; m &= MDELETED; for (mp = dot; mb.mb_threaded ? mp != NULL : mp < &message[msgCount]; mb.mb_threaded ? mp = next_in_thread(mp) : mp++) { if (!(mp->m_flag & MHIDDEN) && (mp->m_flag & m) == f) return mp - message + 1; } if (dot > &message[0]) { for (mp = dot-1; mb.mb_threaded ? mp != NULL : mp >= &message[0]; mb.mb_threaded ? mp = prev_in_thread(mp) : mp--) { if (!(mp->m_flag & MHIDDEN) && (mp->m_flag & m) == f) return mp - message + 1; } } return 0; } /* * See if the passed name sent the passed message number. Return true * if so. */ static int matchsender(char *str, int mesg, int allnet) { if (allnet) { char *cp = nameof(&message[mesg - 1], 0); do { if ((*cp == '@' || *cp == '\0') && (*str == '@' || *str == '\0')) return 1; if (*cp != *str) break; } while (cp++, *str++ != '\0'); return 0; } return !strcmp(str, (value("showname") ? realname : skin) (name1(&message[mesg - 1], 0))); } static int matchmid(char *id, enum idfield idfield, int mesg) { struct name *np; char *cp; if ((cp = hfield("message-id", &message[mesg - 1])) != NULL) { switch (idfield) { case ID_REFERENCES: return msgidcmp(id, cp) == 0; case ID_IN_REPLY_TO: if ((np = extract(id, GREF)) != NULL) do { if (msgidcmp(np->n_name, cp) == 0) return 1; } while ((np = np->n_flink) != NULL); break; } } return 0; } /* * See if the given string matches inside the subject field of the * given message. For the purpose of the scan, we ignore case differences. * If it does, return true. The string search argument is assumed to * have the form "/search-string." If it is of the form "/," we use the * previous search string. */ static char lastscan[128]; static int matchsubj(char *str, int mesg) { struct message *mp; char *cp, *cp2; struct str in, out; int i; str++; if (strlen(str) == 0) { str = lastscan; } else { strncpy(lastscan, str, sizeof lastscan); lastscan[sizeof lastscan - 1]='\0'; } mp = &message[mesg-1]; /* * Now look, ignoring case, for the word in the string. */ if (value("searchheaders") && (cp = strchr(str, ':'))) { *cp++ = '\0'; cp2 = hfield(str, mp); cp[-1] = ':'; str = cp; } else { cp = str; cp2 = hfield("subject", mp); } if (cp2 == NULL) return(0); in.s = cp2; in.l = strlen(cp2); mime_fromhdr(&in, &out, TD_ICONV); i = substr(out.s, cp); free(out.s); return i; } /* * Mark the named message by setting its mark bit. */ void mark(int mesg, int f) { struct message *mp; int i; i = mesg; if (i < 1 || i > msgCount) panic(catgets(catd, CATSET, 129, "Bad message number to mark")); if (mb.mb_threaded == 1 && threadflag) { if ((message[i-1].m_flag & MHIDDEN) == 0) { if (f == MDELETED || (message[i-1].m_flag&MDELETED) == 0) message[i-1].m_flag |= MMARK; } if (message[i-1].m_child) { mp = message[i-1].m_child; mark(mp-message+1, f); for (mp = mp->m_younger; mp; mp = mp->m_younger) mark(mp-message+1, f); } } else message[i-1].m_flag |= MMARK; } /* * Unmark the named message. */ static void unmark(int mesg) { int i; i = mesg; if (i < 1 || i > msgCount) panic(catgets(catd, CATSET, 130, "Bad message number to unmark")); message[i-1].m_flag &= ~MMARK; } /* * Return the message number corresponding to the passed meta character. */ static int metamess(int meta, int f) { int c, m; struct message *mp; c = meta; switch (c) { case '^': /* * First 'good' message left. */ mp = mb.mb_threaded ? threadroot : &message[0]; while (mp < &message[msgCount]) { if (!(mp->m_flag & (MHIDDEN|MKILL)) && (mp->m_flag & MDELETED) == f) return(mp - &message[0] + 1); if (mb.mb_threaded) { mp = next_in_thread(mp); if (mp == NULL) break; } else mp++; } if (!inhook) printf(catgets(catd, CATSET, 131, "No applicable messages\n")); return(-1); case '$': /* * Last 'good message left. */ mp = mb.mb_threaded ? this_in_thread(threadroot, -1) : &message[msgCount-1]; while (mp >= &message[0]) { if (!(mp->m_flag & (MHIDDEN|MKILL)) && (mp->m_flag & MDELETED) == f) return(mp - &message[0] + 1); if (mb.mb_threaded) { mp = prev_in_thread(mp); if (mp == NULL) break; } else mp--; } if (!inhook) printf(catgets(catd, CATSET, 132, "No applicable messages\n")); return(-1); case '.': /* * Current message. */ m = dot - &message[0] + 1; if (dot->m_flag & MHIDDEN || (dot->m_flag & MDELETED) != f) { printf(catgets(catd, CATSET, 133, "%d: Inappropriate message\n"), m); return(-1); } return(m); case ';': /* * Previously current message. */ if (prevdot == NULL) { printf(catgets(catd, CATSET, 228, "No previously current message\n")); return(-1); } m = prevdot - &message[0] + 1; if (prevdot->m_flag&MHIDDEN || (prevdot->m_flag&MDELETED)!=f) { printf(catgets(catd, CATSET, 133, "%d: Inappropriate message\n"), m); return(-1); } return(m); default: printf(catgets(catd, CATSET, 134, "Unknown metachar (%c)\n"), c); return(-1); } } heirloom-mailx-12.5/lzw.c000066400000000000000000000444271155563371200153720ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /*- * Copyright (c) 1985, 1986, 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Diomidis Spinellis and James A. Woods, derived from original * work by Spencer Thomas and Joseph Orost. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* from zopen.c 8.1 (Berkeley) 6/27/93 */ /* from FreeBSD: /repoman/r/ncvs/src/usr.bin/compress/zopen.c,v * 1.5.6.1 2002/07/16 00:52:08 tjr Exp */ /*- * lzw.c - File compression ala IEEE Computer, June 1984. * * Compress authors: * Spencer W. Thomas (decvax!utah-cs!thomas) * Jim McKie (decvax!mcvax!jim) * Steve Davies (decvax!vax135!petsd!peora!srd) * Ken Turkowski (decvax!decwrl!turtlevax!ken) * James A. Woods (decvax!ihnp4!ames!jaw) * Joe Orost (decvax!vax135!petsd!joe) * * Cleaned up and converted to library returning I/O streams by * Diomidis Spinellis . * * Adopted for Heirloom mailx by Gunnar Ritter. * * Sccsid @(#)lzw.c 1.11 (gritter) 3/4/06 */ #include "config.h" #include "rcv.h" #include "extern.h" #include #define BITS 16 /* Default bits. */ #define HSIZE 69001 /* 95% occupancy */ /* A code_int must be able to hold 2**BITS values of type int, and also -1. */ typedef long code_int; typedef long count_int; typedef unsigned char char_type; static char_type magic_header[] = {037, 0235}; /* 1F 9D */ #define BIT_MASK 0x1f /* Defines for third byte of header. */ #define BLOCK_MASK 0x80 /* * Masks 0x40 and 0x20 are free. I think 0x20 should mean that there is * a fourth header byte (for expansion). */ #define INIT_BITS 9 /* Initial number of bits/code. */ #define MAXCODE(n_bits) ((1 << (n_bits)) - 1) struct s_zstate { FILE *zs_fp; /* File stream for I/O */ char zs_mode; /* r or w */ enum { ST_START, ST_MIDDLE, ST_EOF } zs_state; /* State of computation */ unsigned zs_n_bits; /* Number of bits/code. */ unsigned zs_maxbits; /* User settable max # bits/code. */ code_int zs_maxcode; /* Maximum code, given n_bits. */ code_int zs_maxmaxcode; /* Should NEVER generate this code. */ count_int zs_htab[HSIZE]; unsigned short zs_codetab[HSIZE]; code_int zs_hsize; /* For dynamic table sizing. */ code_int zs_free_ent; /* First unused entry. */ /* * Block compression parameters -- after all codes are used up, * and compression rate changes, start over. */ int zs_block_compress; int zs_clear_flg; long zs_ratio; count_int zs_checkpoint; unsigned zs_offset; long zs_in_count; /* Length of input. */ long zs_bytes_out; /* Length of compressed output. */ long zs_out_count; /* # of codes output (for debugging). */ char_type zs_buf[BITS+1]; union { struct { long zs_fcode; code_int zs_ent; code_int zs_hsize_reg; int zs_hshift; } w; /* Write paramenters */ struct { char_type *zs_stackp; int zs_finchar; code_int zs_code, zs_oldcode, zs_incode; int zs_roffset, zs_size; char_type zs_gbuf[BITS+1]; } r; /* Read parameters */ } u; }; /* Definitions to retain old variable names */ #define fp zs->zs_fp #define zmode zs->zs_mode #define state zs->zs_state #define n_bits zs->zs_n_bits #define maxbits zs->zs_maxbits #define maxcode zs->zs_maxcode #define maxmaxcode zs->zs_maxmaxcode #define htab zs->zs_htab #define codetab zs->zs_codetab #define hsize zs->zs_hsize #define free_ent zs->zs_free_ent #define block_compress zs->zs_block_compress #define clear_flg zs->zs_clear_flg #define ratio zs->zs_ratio #define checkpoint zs->zs_checkpoint #define offset zs->zs_offset #define in_count zs->zs_in_count #define bytes_out zs->zs_bytes_out #define out_count zs->zs_out_count #define buf zs->zs_buf #define fcode zs->u.w.zs_fcode #define hsize_reg zs->u.w.zs_hsize_reg #define ent zs->u.w.zs_ent #define hshift zs->u.w.zs_hshift #define stackp zs->u.r.zs_stackp #define finchar zs->u.r.zs_finchar #define code zs->u.r.zs_code #define oldcode zs->u.r.zs_oldcode #define incode zs->u.r.zs_incode #define roffset zs->u.r.zs_roffset #define size zs->u.r.zs_size #define gbuf zs->u.r.zs_gbuf /* * To save much memory, we overlay the table used by compress() with those * used by decompress(). The tab_prefix table is the same size and type as * the codetab. The tab_suffix table needs 2**BITS characters. We get this * from the beginning of htab. The output stack uses the rest of htab, and * contains characters. There is plenty of room for any possible stack * (stack used to be 8000 characters). */ #define htabof(i) htab[i] #define codetabof(i) codetab[i] #define tab_prefixof(i) codetabof(i) #define tab_suffixof(i) ((char_type *)(htab))[i] #define de_stack ((char_type *)&tab_suffixof(1 << BITS)) #define CHECK_GAP 10000 /* Ratio check interval. */ /* * the next two codes should not be changed lightly, as they must not * lie within the contiguous general code space. */ #define FIRST 257 /* First free entry. */ #define CLEAR 256 /* Table clear output code. */ static int output(struct s_zstate *zs, code_int ocode); static code_int getcode(struct s_zstate *zs); static int cl_block(struct s_zstate *zs); static void cl_hash(struct s_zstate *zs, count_int cl_hsize); /*- * Algorithm from "A Technique for High Performance Data Compression", * Terry A. Welch, IEEE Computer Vol 17, No 6 (June 1984), pp 8-19. * * Algorithm: * Modified Lempel-Ziv method (LZW). Basically finds common * substrings and replaces them with a variable size code. This is * deterministic, and can be done on the fly. Thus, the decompression * procedure needs no input table, but tracks the way the table was built. */ /*- * compress write * * Algorithm: use open addressing double hashing (no chaining) on the * prefix code / next character combination. We do a variant of Knuth's * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime * secondary probe. Here, the modular division first probe is gives way * to a faster exclusive-or manipulation. Also do block compression with * an adaptive reset, whereby the code table is cleared when the compression * ratio decreases, but after the table fills. The variable-length output * codes are re-sized at this point, and a special CLEAR code is generated * for the decompressor. Late addition: construct the table according to * file size for noticeable speed improvement on small files. Please direct * questions about this implementation to ames!jaw. */ int zwrite(void *cookie, const char *wbp, int num) { code_int i; int c, disp; struct s_zstate *zs; const unsigned char *bp; unsigned char tmp; int count; if (num == 0) return (0); zs = cookie; zmode = 'w'; count = num; bp = (const unsigned char *)wbp; if (state == ST_MIDDLE) goto middle; state = ST_MIDDLE; maxmaxcode = 1L << maxbits; if (fwrite(magic_header, sizeof(char), sizeof(magic_header), fp) != sizeof(magic_header)) return (-1); tmp = (unsigned char)((maxbits) | block_compress); if (fwrite(&tmp, sizeof(char), sizeof(tmp), fp) != sizeof(tmp)) return (-1); offset = 0; bytes_out = 3; /* Includes 3-byte header mojo. */ out_count = 0; clear_flg = 0; ratio = 0; in_count = 1; checkpoint = CHECK_GAP; maxcode = MAXCODE(n_bits = INIT_BITS); free_ent = ((block_compress) ? FIRST : 256); ent = *bp++; --count; hshift = 0; for (fcode = (long)hsize; fcode < 65536L; fcode *= 2L) hshift++; hshift = 8 - hshift; /* Set hash code range bound. */ hsize_reg = hsize; cl_hash(zs, (count_int)hsize_reg); /* Clear hash table. */ middle: for (i = 0; count--;) { c = *bp++; in_count++; fcode = (long)(((long)c << maxbits) + ent); i = ((c << hshift) ^ ent); /* Xor hashing. */ if (htabof(i) == fcode) { ent = codetabof(i); continue; } else if ((long)htabof(i) < 0) /* Empty slot. */ goto nomatch; disp = hsize_reg - i; /* Secondary hash (after G. Knott). */ if (i == 0) disp = 1; probe: if ((i -= disp) < 0) i += hsize_reg; if (htabof(i) == fcode) { ent = codetabof(i); continue; } if ((long)htabof(i) >= 0) goto probe; nomatch: if (output(zs, (code_int) ent) == -1) return (-1); out_count++; ent = c; if (free_ent < maxmaxcode) { codetabof(i) = free_ent++; /* code -> hashtable */ htabof(i) = fcode; } else if ((count_int)in_count >= checkpoint && block_compress) { if (cl_block(zs) == -1) return (-1); } } return (num); } int zfree(void *cookie) { struct s_zstate *zs; zs = cookie; if (zmode == 'w') { /* Put out the final code. */ if (output(zs, (code_int) ent) == -1) { free(zs); return (-1); } out_count++; if (output(zs, (code_int) - 1) == -1) { free(zs); return (-1); } } free(zs); return (0); } /*- * Output the given code. * Inputs: * code: A n_bits-bit integer. If == -1, then EOF. This assumes * that n_bits =< (long)wordsize - 1. * Outputs: * Outputs code to the file. * Assumptions: * Chars are 8 bits long. * Algorithm: * Maintain a BITS character long buffer (so that 8 codes will * fit in it exactly). Use the VAX insv instruction to insert each * code in turn. When the buffer fills up empty it and start over. */ static char_type lmask[9] = {0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00}; static char_type rmask[9] = {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff}; static int output(struct s_zstate *zs, code_int ocode) { int r_off; unsigned bits; char_type *bp; r_off = offset; bits = n_bits; bp = buf; if (ocode >= 0) { /* Get to the first byte. */ bp += (r_off >> 3); r_off &= 7; /* * Since ocode is always >= 8 bits, only need to mask the first * hunk on the left. */ *bp = (*bp & rmask[r_off]) | ((ocode << r_off) & lmask[r_off]); bp++; bits -= (8 - r_off); ocode >>= 8 - r_off; /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */ if (bits >= 8) { *bp++ = ocode; ocode >>= 8; bits -= 8; } /* Last bits. */ if (bits) *bp = ocode; offset += n_bits; if (offset == (n_bits << 3)) { bp = buf; bits = n_bits; bytes_out += bits; if (fwrite(bp, sizeof(char), bits, fp) != bits) return (-1); bp += bits; bits = 0; offset = 0; } /* * If the next entry is going to be too big for the ocode size, * then increase it, if possible. */ if (free_ent > maxcode || (clear_flg > 0)) { /* * Write the whole buffer, because the input side won't * discover the size increase until after it has read it. */ if (offset > 0) { if (fwrite(buf, 1, n_bits, fp) != n_bits) return (-1); bytes_out += n_bits; } offset = 0; if (clear_flg) { maxcode = MAXCODE(n_bits = INIT_BITS); clear_flg = 0; } else { n_bits++; if (n_bits == maxbits) maxcode = maxmaxcode; else maxcode = MAXCODE(n_bits); } } } else { /* At EOF, write the rest of the buffer. */ if (offset > 0) { offset = (offset + 7) / 8; if (fwrite(buf, 1, offset, fp) != offset) return (-1); bytes_out += offset; } offset = 0; } return (0); } /* * Decompress read. This routine adapts to the codes in the file building * the "string" table on-the-fly; requiring no table to be stored in the * compressed file. The tables used herein are shared with those of the * compress() routine. See the definitions above. */ int zread(void *cookie, char *rbp, int num) { unsigned count; struct s_zstate *zs; unsigned char *bp, header[3]; if (num == 0) return (0); zs = cookie; count = num; bp = (unsigned char *)rbp; switch (state) { case ST_START: state = ST_MIDDLE; break; case ST_MIDDLE: goto middle; case ST_EOF: goto eof; } /* Check the magic number */ if (fread(header, sizeof(char), sizeof(header), fp) != sizeof(header) || memcmp(header, magic_header, sizeof(magic_header)) != 0) { return (-1); } maxbits = header[2]; /* Set -b from file. */ block_compress = maxbits & BLOCK_MASK; maxbits &= BIT_MASK; maxmaxcode = 1L << maxbits; if (maxbits > BITS) { return (-1); } /* As above, initialize the first 256 entries in the table. */ maxcode = MAXCODE(n_bits = INIT_BITS); for (code = 255; code >= 0; code--) { tab_prefixof(code) = 0; tab_suffixof(code) = (char_type) code; } free_ent = block_compress ? FIRST : 256; finchar = oldcode = getcode(zs); if (oldcode == -1) /* EOF already? */ return (0); /* Get out of here */ /* First code must be 8 bits = char. */ *bp++ = (unsigned char)finchar; count--; stackp = de_stack; while ((code = getcode(zs)) > -1) { if ((code == CLEAR) && block_compress) { for (code = 255; code >= 0; code--) tab_prefixof(code) = 0; clear_flg = 1; free_ent = FIRST - 1; if ((code = getcode(zs)) == -1) /* O, untimely death! */ break; } incode = code; /* Special case for KwKwK string. */ if (code >= free_ent) { *stackp++ = finchar; code = oldcode; } /* Generate output characters in reverse order. */ while (code >= 256) { *stackp++ = tab_suffixof(code); code = tab_prefixof(code); } *stackp++ = finchar = tab_suffixof(code); /* And put them out in forward order. */ middle: do { if (count-- == 0) return (num); *bp++ = *--stackp; } while (stackp > de_stack); /* Generate the new entry. */ if ((code = free_ent) < maxmaxcode) { tab_prefixof(code) = (unsigned short) oldcode; tab_suffixof(code) = finchar; free_ent = code + 1; } /* Remember previous code. */ oldcode = incode; } state = ST_EOF; eof: return (num - count); } /*- * Read one code from the standard input. If EOF, return -1. * Inputs: * stdin * Outputs: * code or -1 is returned. */ static code_int getcode(struct s_zstate *zs) { code_int gcode; int r_off, bits; char_type *bp; bp = gbuf; if (clear_flg > 0 || roffset >= size || free_ent > maxcode) { /* * If the next entry will be too big for the current gcode * size, then we must increase the size. This implies reading * a new buffer full, too. */ if (free_ent > maxcode) { n_bits++; if (n_bits == maxbits) /* Won't get any bigger now. */ maxcode = maxmaxcode; else maxcode = MAXCODE(n_bits); } if (clear_flg > 0) { maxcode = MAXCODE(n_bits = INIT_BITS); clear_flg = 0; } size = fread(gbuf, 1, n_bits, fp); if (size <= 0) /* End of file. */ return (-1); roffset = 0; /* Round size down to integral number of codes. */ size = (size << 3) - (n_bits - 1); } r_off = roffset; bits = n_bits; /* Get to the first byte. */ bp += (r_off >> 3); r_off &= 7; /* Get first part (low order bits). */ gcode = (*bp++ >> r_off); bits -= (8 - r_off); r_off = 8 - r_off; /* Now, roffset into gcode word. */ /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */ if (bits >= 8) { gcode |= *bp++ << r_off; r_off += 8; bits -= 8; } /* High order bits. */ gcode |= (*bp & rmask[bits]) << r_off; roffset += n_bits; return (gcode); } static int cl_block ( /* Table clear for block compress. */ struct s_zstate *zs ) { long rat; checkpoint = in_count + CHECK_GAP; if (in_count > 0x007fffff) { /* Shift will overflow. */ rat = bytes_out >> 8; if (rat == 0) /* Don't divide by zero. */ rat = 0x7fffffff; else rat = in_count / rat; } else rat = (in_count << 8) / bytes_out; /* 8 fractional bits. */ if (rat > ratio) ratio = rat; else { ratio = 0; cl_hash(zs, (count_int) hsize); free_ent = FIRST; clear_flg = 1; if (output(zs, (code_int) CLEAR) == -1) return (-1); } return (0); } static void cl_hash ( /* Reset code table. */ struct s_zstate *zs, count_int cl_hsize ) { count_int *htab_p; long i, m1; m1 = -1; htab_p = htab + cl_hsize; i = cl_hsize - 16; do { /* Might use Sys V memset(3) here. */ *(htab_p - 16) = m1; *(htab_p - 15) = m1; *(htab_p - 14) = m1; *(htab_p - 13) = m1; *(htab_p - 12) = m1; *(htab_p - 11) = m1; *(htab_p - 10) = m1; *(htab_p - 9) = m1; *(htab_p - 8) = m1; *(htab_p - 7) = m1; *(htab_p - 6) = m1; *(htab_p - 5) = m1; *(htab_p - 4) = m1; *(htab_p - 3) = m1; *(htab_p - 2) = m1; *(htab_p - 1) = m1; htab_p -= 16; } while ((i -= 16) >= 0); for (i += 16; i > 0; i--) *--htab_p = m1; } #undef fp void * zalloc(FILE *fp) { #define bits BITS struct s_zstate *zs; zs = scalloc(1, sizeof *zs); maxbits = bits ? bits : BITS; /* User settable max # bits/code. */ maxmaxcode = 1L << maxbits; /* Should NEVER generate this code. */ hsize = HSIZE; /* For dynamic table sizing. */ free_ent = 0; /* First unused entry. */ block_compress = BLOCK_MASK; clear_flg = 0; ratio = 0; checkpoint = CHECK_GAP; in_count = 1; /* Length of input. */ out_count = 0; /* # of codes output (for debugging). */ state = ST_START; roffset = 0; size = 0; zs->zs_fp = fp; return zs; } heirloom-mailx-12.5/macro.c000066400000000000000000000202121155563371200156410ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 2004 * Gunnar Ritter. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Gunnar Ritter * and his contributors. * 4. Neither the name of Gunnar Ritter nor the names of his contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)macro.c 1.13 (gritter) 3/4/06"; #endif #endif /* not lint */ #include "config.h" #include "rcv.h" #include "extern.h" /* * Mail -- a mail program * * Macros. */ #define MAPRIME 29 struct line { struct line *l_next; char *l_line; size_t l_linesize; }; struct macro { struct macro *ma_next; char *ma_name; struct line *ma_contents; enum maflags { MA_NOFLAGS, MA_ACCOUNT } ma_flags; }; static struct macro *macros[MAPRIME]; static struct macro *accounts[MAPRIME]; #define mahash(cp) (pjw(cp) % MAPRIME) static void undef1(const char *name, struct macro **table); static int maexec(struct macro *mp); static int closingangle(const char *cp); static struct macro *malook(const char *name, struct macro *data, struct macro **table); static void freelines(struct line *lp); static void list0(FILE *fp, struct line *lp); static int list1(FILE *fp, struct macro **table); int cdefine(void *v) { char **args = v; if (args[0] == NULL) { fprintf(stderr, "Missing macro name to define.\n"); return 1; } if (args[1] == NULL || strcmp(args[1], "{") || args[2] != NULL) { fprintf(stderr, "Syntax is: define {\n"); return 1; } return define1(args[0], 0); } int define1(const char *name, int account) { struct macro *mp; struct line *lp, *lst = NULL, *lnd = NULL; char *linebuf = NULL; size_t linesize = 0; int n; mp = scalloc(1, sizeof *mp); mp->ma_name = sstrdup(name); if (account) mp->ma_flags |= MA_ACCOUNT; for (;;) { n = 0; for (;;) { n = readline_restart(input, &linebuf, &linesize, n); if (n < 0) break; if (n == 0 || linebuf[n-1] != '\\') break; linebuf[n-1] = '\n'; } if (n < 0) { fprintf(stderr, "Unterminated %s definition: \"%s\".\n", account ? "account" : "macro", mp->ma_name); if (sourcing) unstack(); free(mp->ma_name); free(mp); return 1; } if (closingangle(linebuf)) break; lp = scalloc(1, sizeof *lp); lp->l_linesize = n+1; lp->l_line = smalloc(lp->l_linesize); memcpy(lp->l_line, linebuf, lp->l_linesize); lp->l_line[n] = '\0'; if (lst && lnd) { lnd->l_next = lp; lnd = lp; } else lst = lnd = lp; } mp->ma_contents = lst; if (malook(mp->ma_name, mp, account ? accounts : macros) != NULL) { if (!account) { fprintf(stderr, "A macro named \"%s\" already exists.\n", mp->ma_name); freelines(mp->ma_contents); free(mp->ma_name); free(mp); return 1; } undef1(mp->ma_name, accounts); malook(mp->ma_name, mp, accounts); } return 0; } int cundef(void *v) { char **args = v; if (*args == NULL) { fprintf(stderr, "Missing macro name to undef.\n"); return 1; } do undef1(*args, macros); while (*++args); return 0; } static void undef1(const char *name, struct macro **table) { struct macro *mp; if ((mp = malook(name, NULL, table)) != NULL) { freelines(mp->ma_contents); free(mp->ma_name); mp->ma_name = NULL; } } int ccall(void *v) { char **args = v; struct macro *mp; if (args[0] == NULL || args[1] != NULL && args[2] != NULL) { fprintf(stderr, "Syntax is: call \n"); return 1; } if ((mp = malook(*args, NULL, macros)) == NULL) { fprintf(stderr, "Undefined macro called: \"%s\"\n", *args); return 1; } return maexec(mp); } int callaccount(const char *name) { struct macro *mp; if ((mp = malook(name, NULL, accounts)) == NULL) return CBAD; return maexec(mp); } int callhook(const char *name, int newmail) { struct macro *mp; char *var, *cp; int len, r; var = ac_alloc(len = strlen(name) + 13); snprintf(var, len, "folder-hook-%s", name); if ((cp = value(var)) == NULL && (cp = value("folder-hook")) == NULL) return 0; if ((mp = malook(cp, NULL, macros)) == NULL) { fprintf(stderr, "Cannot call hook for folder \"%s\": " "Macro \"%s\" does not exist.\n", name, cp); return 1; } inhook = newmail ? 3 : 1; r = maexec(mp); inhook = 0; return r; } static int maexec(struct macro *mp) { struct line *lp; const char *sp; char *copy, *cp; int r = 0; unset_allow_undefined = 1; for (lp = mp->ma_contents; lp; lp = lp->l_next) { sp = lp->l_line; while (sp < &lp->l_line[lp->l_linesize] && (blankchar(*sp&0377) || *sp == '\n' || *sp == '\0')) sp++; if (sp == &lp->l_line[lp->l_linesize]) continue; cp = copy = smalloc(lp->l_linesize + (lp->l_line - sp)); do *cp++ = *sp != '\n' ? *sp : ' '; while (++sp < &lp->l_line[lp->l_linesize]); r = execute(copy, 0, lp->l_linesize); free(copy); } unset_allow_undefined = 0; return r; } static int closingangle(const char *cp) { while (spacechar(*cp&0377)) cp++; if (*cp++ != '}') return 0; while (spacechar(*cp&0377)) cp++; return *cp == '\0'; } static struct macro * malook(const char *name, struct macro *data, struct macro **table) { struct macro *mp; unsigned h; mp = table[h = mahash(name)]; while (mp != NULL) { if (mp->ma_name && strcmp(mp->ma_name, name) == 0) break; mp = mp->ma_next; } if (data) { if (mp != NULL) return mp; data->ma_next = table[h]; table[h] = data; } return mp; } static void freelines(struct line *lp) { struct line *lq = NULL; while (lp) { free(lp->l_line); free(lq); lq = lp; lp = lp->l_next; } free(lq); } int listaccounts(FILE *fp) { return list1(fp, accounts); } static void list0(FILE *fp, struct line *lp) { const char *sp; int c; for (sp = lp->l_line; sp < &lp->l_line[lp->l_linesize]; sp++) { if ((c = *sp&0377) != '\0') { if ((c = *sp&0377) == '\n') putc('\\', fp); putc(c, fp); } } putc('\n', fp); } static int list1(FILE *fp, struct macro **table) { struct macro **mp, *mq; struct line *lp; int mc = 0; for (mp = table; mp < &table[MAPRIME]; mp++) for (mq = *mp; mq; mq = mq->ma_next) if (mq->ma_name) { if (mc++) fputc('\n', fp); fprintf(fp, "%s %s {\n", table == accounts ? "account" : "define", mq->ma_name); for (lp = mq->ma_contents; lp; lp = lp->l_next) list0(fp, lp); fputs("}\n", fp); } return mc; } /*ARGSUSED*/ int cdefines(void *v) { FILE *fp; char *cp; int mc; if ((fp = Ftemp(&cp, "Ra", "w+", 0600, 1)) == NULL) { perror("tmpfile"); return 1; } rm(cp); Ftfree(&cp); mc = list1(fp, macros); if (mc) try_pager(fp); Fclose(fp); return 0; } void delaccount(const char *name) { undef1(name, accounts); } heirloom-mailx-12.5/maildir.c000066400000000000000000000466031155563371200161750ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 2004 * Gunnar Ritter. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Gunnar Ritter * and his contributors. * 4. Neither the name of Gunnar Ritter nor the names of his contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)maildir.c 1.20 (gritter) 12/28/06"; #endif #endif /* not lint */ #include "config.h" #include "rcv.h" #include "extern.h" #include #include #include #include #include /* * Mail -- a mail program * * Maildir folder support. */ static struct mditem { struct message *md_data; unsigned md_hash; } *mdtable; static long mdprime; static sigjmp_buf maildirjmp; static int maildir_setfile1(const char *name, int newmail, int omsgCount); static int mdcmp(const void *a, const void *b); static int subdir(const char *name, const char *sub, int newmail); static void cleantmp(const char *name); static void append(const char *name, const char *sub, const char *fn); static void readin(const char *name, struct message *m); static void maildir_update(void); static void move(struct message *m); static char *mkname(time_t t, enum mflag f, const char *pref); static void maildircatch(int s); static enum okay maildir_append1(const char *name, FILE *fp, off_t off1, long size, enum mflag flag); static enum okay trycreate(const char *name); static enum okay mkmaildir(const char *name); static struct message *mdlook(const char *name, struct message *data); static void mktable(void); static enum okay subdir_remove(const char *name, const char *sub); int maildir_setfile(const char *name, int newmail, int isedit) { sighandler_type saveint; struct cw cw; int i = -1, omsgCount; (void)&saveint; (void)&i; omsgCount = msgCount; if (cwget(&cw) == STOP) { fprintf(stderr, "Fatal: Cannot open current directory\n"); return -1; } if (!newmail) quit(); saveint = safe_signal(SIGINT, SIG_IGN); if (chdir(name) < 0) { fprintf(stderr, "Cannot change directory to \"%s\".\n", name); cwrelse(&cw); return -1; } if (!newmail) { edit = isedit; if (mb.mb_itf) { fclose(mb.mb_itf); mb.mb_itf = NULL; } if (mb.mb_otf) { fclose(mb.mb_otf); mb.mb_otf = NULL; } initbox(name); mb.mb_type = MB_MAILDIR; } mdtable = NULL; if (sigsetjmp(maildirjmp, 1) == 0) { if (newmail) mktable(); if (saveint != SIG_IGN) safe_signal(SIGINT, maildircatch); i = maildir_setfile1(name, newmail, omsgCount); } if (newmail) free(mdtable); safe_signal(SIGINT, saveint); if (i < 0) { mb.mb_type = MB_VOID; *mailname = '\0'; msgCount = 0; } if (cwret(&cw) == STOP) { fputs("Fatal: Cannot change back to current directory.\n", stderr); abort(); } cwrelse(&cw); setmsize(msgCount); if (newmail && mb.mb_sorted && msgCount > omsgCount) { mb.mb_threaded = 0; sort((void *)-1); } if (!newmail) sawcom = 0; if (!newmail && !edit && msgCount == 0) { if (mb.mb_type == MB_MAILDIR && value("emptystart") == NULL) fprintf(stderr, "No mail at %s\n", name); return 1; } if (newmail && msgCount > omsgCount) newmailinfo(omsgCount); return 0; } static int maildir_setfile1(const char *name, int newmail, int omsgCount) { int i; if (!newmail) cleantmp(name); mb.mb_perm = Rflag ? 0 : MB_DELE; if ((i = subdir(name, "cur", newmail)) != 0) return i; if ((i = subdir(name, "new", newmail)) != 0) return i; append(name, NULL, NULL); for (i = newmail?omsgCount:0; i < msgCount; i++) readin(name, &message[i]); if (newmail) { if (msgCount > omsgCount) qsort(&message[omsgCount], msgCount - omsgCount, sizeof *message, mdcmp); } else { if (msgCount) qsort(message, msgCount, sizeof *message, mdcmp); } return msgCount; } /* * In combination with the names from mkname(), this comparison function * ensures that the order of messages in a maildir folder created by mailx * remains always the same. In effect, if a mbox folder is transferred to * a maildir folder by 'copy *', the order of the messages in mailx will * not change. */ static int mdcmp(const void *a, const void *b) { long i; if ((i = ((struct message *)a)->m_time - ((struct message *)b)->m_time) == 0) i = strcmp(&((struct message *)a)->m_maildir_file[4], &((struct message *)b)->m_maildir_file[4]); return i; } static int subdir(const char *name, const char *sub, int newmail) { DIR *dirfd; struct dirent *dp; if ((dirfd = opendir(sub)) == NULL) { fprintf(stderr, "Cannot open directory \"%s/%s\".\n", name, sub); return -1; } if (access(sub, W_OK) < 0) mb.mb_perm = 0; while ((dp = readdir(dirfd)) != NULL) { if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) continue; if (dp->d_name[0] == '.') continue; if (!newmail || mdlook(dp->d_name, NULL) == NULL) append(name, sub, dp->d_name); } closedir(dirfd); return 0; } static void cleantmp(const char *name) { struct stat st; DIR *dirfd; struct dirent *dp; char *fn = NULL; size_t fnsz = 0, ssz; time_t now; if ((dirfd = opendir("tmp")) == NULL) return; time(&now); while ((dp = readdir(dirfd)) != NULL) { if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) continue; if (dp->d_name[0] == '.') continue; if ((ssz = strlen(dp->d_name)) + 5 > fnsz) { free(fn); fn = smalloc(fnsz = ssz + 40); } strcpy(fn, "tmp/"); strcpy(&fn[4], dp->d_name); if (stat(fn, &st) < 0) continue; if (st.st_atime + 36*3600 < now) unlink(fn); } free(fn); closedir(dirfd); } static void append(const char *name, const char *sub, const char *fn) { struct message *m; size_t sz; time_t t = 0; enum mflag f = MUSED|MNOFROM|MNEWEST; const char *cp; char *xp; if (fn && sub) { if (strcmp(sub, "new") == 0) f |= MNEW; t = strtol(fn, &xp, 10); if ((cp = strrchr(xp, ',')) != NULL && cp > &xp[2] && cp[-1] == '2' && cp[-2] == ':') { while (*++cp) { switch (*cp) { case 'F': f |= MFLAGGED; break; case 'R': f |= MANSWERED; break; case 'S': f |= MREAD; break; case 'T': f |= MDELETED; break; case 'D': f |= MDRAFT; break; } } } } if (msgCount + 1 >= msgspace) { const int chunk = 64; message = srealloc(message, (msgspace += chunk) * sizeof *message); memset(&message[msgCount], 0, chunk * sizeof *message); } if (fn == NULL || sub == NULL) return; m = &message[msgCount++]; m->m_maildir_file = smalloc((sz = strlen(sub)) + strlen(fn) + 2); strcpy(m->m_maildir_file, sub); m->m_maildir_file[sz] = '/'; strcpy(&m->m_maildir_file[sz+1], fn); m->m_time = t; m->m_flag = f; m->m_maildir_hash = ~pjw(fn); return; } static void readin(const char *name, struct message *m) { char *buf, *bp; size_t bufsize, buflen, count; long size = 0, lines = 0; off_t offset; FILE *fp; int emptyline = 0; if ((fp = Fopen(m->m_maildir_file, "r")) == NULL) { fprintf(stderr, "Cannot read \"%s/%s\" for message %d\n", name, m->m_maildir_file, m - &message[0] + 1); m->m_flag |= MHIDDEN; return; } buf = smalloc(bufsize = LINESIZE); buflen = 0; count = fsize(fp); fseek(mb.mb_otf, 0L, SEEK_END); offset = ftell(mb.mb_otf); while (fgetline(&buf, &bufsize, &count, &buflen, fp, 1) != NULL) { bp = buf; if (buf[0] == 'F' && buf[1] == 'r' && buf[2] == 'o' && buf[3] == 'm' && buf[4] == ' ') { putc('>', mb.mb_otf); size++; } lines++; size += fwrite(bp, 1, buflen, mb.mb_otf); emptyline = *bp == '\n'; } if (!emptyline) { putc('\n', mb.mb_otf); lines++; size++; } Fclose(fp); fflush(mb.mb_otf); m->m_size = m->m_xsize = size; m->m_lines = m->m_xlines = lines; m->m_block = mailx_blockof(offset); m->m_offset = mailx_offsetof(offset); free(buf); substdate(m); } void maildir_quit(void) { sighandler_type saveint; struct cw cw; (void)&saveint; if (cwget(&cw) == STOP) { fprintf(stderr, "Fatal: Cannot open current directory\n"); return; } saveint = safe_signal(SIGINT, SIG_IGN); if (chdir(mailname) < 0) { fprintf(stderr, "Cannot change directory to \"%s\".\n", mailname); cwrelse(&cw); return; } if (sigsetjmp(maildirjmp, 1) == 0) { if (saveint != SIG_IGN) safe_signal(SIGINT, maildircatch); maildir_update(); } safe_signal(SIGINT, saveint); if (cwret(&cw) == STOP) { fputs("Fatal: Cannot change back to current directory.\n", stderr); abort(); } cwrelse(&cw); } static void maildir_update(void) { FILE *readstat = NULL; struct message *m; int dodel, c, gotcha = 0, held = 0, modflags = 0; if (mb.mb_perm == 0) goto free; if (Tflag != NULL) { if ((readstat = Zopen(Tflag, "w", NULL)) == NULL) Tflag = NULL; } if (!edit) { holdbits(); for (m = &message[0], c = 0; m < &message[msgCount]; m++) { if (m->m_flag & MBOX) c++; } if (c > 0) if (makembox() == STOP) goto bypass; } for (m = &message[0], gotcha=0, held=0; m < &message[msgCount]; m++) { if (readstat != NULL && (m->m_flag & (MREAD|MDELETED)) != 0) { char *id; if ((id = hfield("message-id", m)) != NULL || (id = hfield("article-id", m)) != NULL) fprintf(readstat, "%s\n", id); } if (edit) dodel = m->m_flag & MDELETED; else dodel = !((m->m_flag&MPRESERVE) || (m->m_flag&MTOUCH) == 0); if (dodel) { if (unlink(m->m_maildir_file) < 0) fprintf(stderr, "Cannot delete file \"%s/%s\" " "for message %d.\n", mailname, m->m_maildir_file, m - &message[0] + 1); else gotcha++; } else { if ((m->m_flag&(MREAD|MSTATUS)) == (MREAD|MSTATUS) || m->m_flag & (MNEW|MBOXED|MSAVED|MSTATUS| MFLAG|MUNFLAG| MANSWER|MUNANSWER| MDRAFT|MUNDRAFT)) { move(m); modflags++; } held++; } } bypass: if (readstat != NULL) Fclose(readstat); if ((gotcha || modflags) && edit) { printf(catgets(catd, CATSET, 168, "\"%s\" "), mailname); printf(value("bsdcompat") || value("bsdmsgs") ? catgets(catd, CATSET, 170, "complete\n") : catgets(catd, CATSET, 212, "updated.\n")); } else if (held && !edit && mb.mb_perm != 0) { if (held == 1) printf(catgets(catd, CATSET, 155, "Held 1 message in %s\n"), mailname); else if (held > 1) printf(catgets(catd, CATSET, 156, "Held %d messages in %s\n"), held, mailname); } fflush(stdout); free: for (m = &message[0]; m < &message[msgCount]; m++) free(m->m_maildir_file); } static void move(struct message *m) { char *fn, *new; fn = mkname(0, m->m_flag, &m->m_maildir_file[4]); new = savecat("cur/", fn); if (strcmp(m->m_maildir_file, new) == 0) return; if (link(m->m_maildir_file, new) < 0) { fprintf(stderr, "Cannot link \"%s/%s\" to \"%s/%s\": " "message %d not touched.\n", mailname, m->m_maildir_file, mailname, new, m - &message[0] + 1); return; } if (unlink(m->m_maildir_file) < 0) fprintf(stderr, "Cannot unlink \"%s/%s\".\n", mailname, m->m_maildir_file); } static char * mkname(time_t t, enum mflag f, const char *pref) { static unsigned long count; static pid_t mypid; char *cp; static char *node; int size, n, i; if (pref == NULL) { if (mypid == 0) mypid = getpid(); if (node == NULL) { cp = nodename(0); n = size = 0; do { if (n < size + 8) node = srealloc(node, size += 20); switch (*cp) { case '/': node[n++] = '\\', node[n++] = '0', node[n++] = '5', node[n++] = '7'; break; case ':': node[n++] = '\\', node[n++] = '0', node[n++] = '7', node[n++] = '2'; break; default: node[n++] = *cp; } } while (*cp++); } size = 60 + strlen(node); cp = salloc(size); n = snprintf(cp, size, "%lu.%06lu_%06lu.%s:2,", (unsigned long)t, (unsigned long)mypid, ++count, node); } else { size = (n = strlen(pref)) + 13; cp = salloc(size); strcpy(cp, pref); for (i = n; i > 3; i--) if (cp[i-1] == ',' && cp[i-2] == '2' && cp[i-3] == ':') { n = i; break; } if (i <= 3) { strcpy(&cp[n], ":2,"); n += 3; } } if (n < size - 7) { if (f & MDRAFTED) cp[n++] = 'D'; if (f & MFLAGGED) cp[n++] = 'F'; if (f & MANSWERED) cp[n++] = 'R'; if (f & MREAD) cp[n++] = 'S'; if (f & MDELETED) cp[n++] = 'T'; cp[n] = '\0'; } return cp; } static void maildircatch(int s) { siglongjmp(maildirjmp, s); } enum okay maildir_append(const char *name, FILE *fp) { char *buf, *bp, *lp; size_t bufsize, buflen, count; off_t off1 = -1, offs; int inhead = 1; int flag = MNEW|MNEWEST; long size = 0; enum okay ok; if (mkmaildir(name) != OKAY) return STOP; buf = smalloc(bufsize = LINESIZE); buflen = 0; count = fsize(fp); offs = ftell(fp); do { bp = fgetline(&buf, &bufsize, &count, &buflen, fp, 1); if (bp == NULL || strncmp(buf, "From ", 5) == 0) { if (off1 != (off_t)-1) { ok = maildir_append1(name, fp, off1, size, flag); if (ok == STOP) return STOP; fseek(fp, offs+buflen, SEEK_SET); } off1 = offs + buflen; size = 0; inhead = 1; flag = MNEW; } else size += buflen; offs += buflen; if (bp && buf[0] == '\n') inhead = 0; else if (bp && inhead && ascncasecmp(buf, "status", 6) == 0) { lp = &buf[6]; while (whitechar(*lp&0377)) lp++; if (*lp == ':') while (*++lp != '\0') switch (*lp) { case 'R': flag |= MREAD; break; case 'O': flag &= ~MNEW; break; } } else if (bp && inhead && ascncasecmp(buf, "x-status", 8) == 0) { lp = &buf[8]; while (whitechar(*lp&0377)) lp++; if (*lp == ':') while (*++lp != '\0') switch (*lp) { case 'F': flag |= MFLAGGED; break; case 'A': flag |= MANSWERED; break; case 'T': flag |= MDRAFTED; break; } } } while (bp != NULL); free(buf); return OKAY; } static enum okay maildir_append1(const char *name, FILE *fp, off_t off1, long size, enum mflag flag) { const int attempts = 43200; struct stat st; char buf[4096]; char *fn, *tmp, *new; FILE *op; long n, z; int i; time_t now; for (i = 0; i < attempts; i++) { time(&now); fn = mkname(now, flag, NULL); tmp = salloc(n = strlen(name) + strlen(fn) + 6); snprintf(tmp, n, "%s/tmp/%s", name, fn); if (stat(tmp, &st) < 0 && errno == ENOENT) break; sleep(2); } if (i >= attempts) { fprintf(stderr, "Cannot create unique file name in \"%s/tmp\".\n", name); return STOP; } if ((op = Fopen(tmp, "w")) == NULL) { fprintf(stderr, "Cannot write to \"%s\".\n", tmp); return STOP; } fseek(fp, off1, SEEK_SET); while (size > 0) { z = size > sizeof buf ? sizeof buf : size; if ((n = fread(buf, 1, z, fp)) != z || fwrite(buf, 1, n, op) != n) { fprintf(stderr, "Error writing to \"%s\".\n", tmp); Fclose(op); unlink(tmp); return STOP; } size -= n; } Fclose(op); new = salloc(n = strlen(name) + strlen(fn) + 6); snprintf(new, n, "%s/new/%s", name, fn); if (link(tmp, new) < 0) { fprintf(stderr, "Cannot link \"%s\" to \"%s\".\n", tmp, new); return STOP; } if (unlink(tmp) < 0) fprintf(stderr, "Cannot unlink \"%s\".\n", tmp); return OKAY; } static enum okay trycreate(const char *name) { struct stat st; if (stat(name, &st) == 0) { if (!S_ISDIR(st.st_mode)) { fprintf(stderr, "\"%s\" is not a directory.\n", name); return STOP; } } else if (makedir(name) != OKAY) { fprintf(stderr, "Cannot create directory \"%s\".\n", name); return STOP; } else imap_created_mailbox++; return OKAY; } static enum okay mkmaildir(const char *name) { char *np; size_t sz; enum okay ok = STOP; if (trycreate(name) == OKAY) { np = ac_alloc((sz = strlen(name)) + 5); strcpy(np, name); strcpy(&np[sz], "/tmp"); if (trycreate(np) == OKAY) { strcpy(&np[sz], "/new"); if (trycreate(np) == OKAY) { strcpy(&np[sz], "/cur"); if (trycreate(np) == OKAY) ok = OKAY; } } ac_free(np); } return ok; } static struct message * mdlook(const char *name, struct message *data) { struct mditem *md; unsigned c, h, n = 0; if (data && data->m_maildir_hash) h = ~data->m_maildir_hash; else h = pjw(name); h %= mdprime; md = &mdtable[c = h]; while (md->md_data != NULL) { if (strcmp(&md->md_data->m_maildir_file[4], name) == 0) break; c += n&1 ? -((n+1)/2) * ((n+1)/2) : ((n+1)/2) * ((n+1)/2); n++; while (c >= mdprime) c -= mdprime; md = &mdtable[c]; } if (data != NULL && md->md_data == NULL) md->md_data = data; return md->md_data ? md->md_data : NULL; } static void mktable(void) { int i; mdprime = nextprime(msgCount); mdtable = scalloc(mdprime, sizeof *mdtable); for (i = 0; i < msgCount; i++) mdlook(&message[i].m_maildir_file[4], &message[i]); } static enum okay subdir_remove(const char *name, const char *sub) { char *path; int pathsize, pathend, namelen, sublen, n; DIR *dirfd; struct dirent *dp; namelen = strlen(name); sublen = strlen(sub); path = smalloc(pathsize = namelen + sublen + 30); strcpy(path, name); path[namelen] = '/'; strcpy(&path[namelen+1], sub); path[namelen+sublen+1] = '/'; path[pathend = namelen + sublen + 2] = '\0'; if ((dirfd = opendir(path)) == NULL) { perror(path); free(path); return STOP; } while ((dp = readdir(dirfd)) != NULL) { if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) continue; if (dp->d_name[0] == '.') continue; n = strlen(dp->d_name); if (pathend + n + 1 > pathsize) path = srealloc(path, pathsize = pathend + n + 30); strcpy(&path[pathend], dp->d_name); if (unlink(path) < 0) { perror(path); closedir(dirfd); free(path); return STOP; } } closedir(dirfd); path[pathend] = '\0'; if (rmdir(path) < 0) { perror(path); free(path); return STOP; } free(path); return OKAY; } enum okay maildir_remove(const char *name) { if (subdir_remove(name, "tmp") == STOP || subdir_remove(name, "new") == STOP || subdir_remove(name, "cur") == STOP) return STOP; if (rmdir(name) < 0) { perror(name); return STOP; } return OKAY; } heirloom-mailx-12.5/mailx.1000066400000000000000000004071331155563371200156030ustar00rootroot00000000000000'\" t .\" Copyright (c) 1980, 1990, 1993 .\" The Regents of the University of California. All rights reserved. .\" Copyright (c) 2000 .\" Gunnar Ritter. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. All advertising materials mentioning features or use of this software .\" must display the following acknowledgement: .\" This product includes software developed by the University of .\" California, Berkeley and its contributors. .\" This product includes software developed by Gunnar Ritter .\" and his contributors. .\" 4. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS '\fIAS IS\fR' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" Sccsid: @(#)mailx.1 2.328 (gritter) 6/15/09 .\" .TH MAILX 1 "6/15/09" "Heirloom mailx 12.5" "User Commands" .SH NAME mailx \- send and receive Internet mail .SH SYNOPSIS .PD 0 .HP .ad l \fBmailx\fR [\fB\-BDdEFintv~\fR] [\fB\-s\fI\ subject\fR] [\fB\-a\fI\ attachment\fR ] [\fB\-c\fI\ cc-addr\fR] [\fB\-b\fI\ bcc-addr\fR] [\fB\-r\fI\ from-addr\fR] [\fB\-h\fI\ hops\fR] [\fB\-A\fI\ account\fR] [\fB\-S\fI\ variable\fR[\fB=\fIvalue\fR]] \fIto-addr\fR .\ .\ . .HP .ad l \fBmailx\fR [\fB\-BDdeEHiInNRv~\fR] [\fB\-T\fI\ name\fR] [\fB\-A\fI\ account\fR] [\fB\-S\fI\ variable\fR[\fB=\fIvalue\fR]] \fB\-f\fR [\fIname\fR] .HP .ad l \fBmailx\fR [\fB\-BDdeEinNRv~\fR] [\fB\-A\fI\ account\fR] [\fB\-S\fI\ variable\fR[\fB=\fIvalue\fR]] [\fB\-u\fI\ user\fR] .br .PD .ad b .SH DESCRIPTION \fIMailx\fR is an intelligent mail processing system, which has a command syntax reminiscent of .IR ed (1) with lines replaced by messages. It is based on Berkeley Mail 8.1, is intended to provide the functionality of the POSIX .B mailx command, and offers extensions for MIME, IMAP, POP3, SMTP, and S/MIME. .I Mailx provides enhanced features for interactive use, such as caching and disconnected operation for IMAP, message threading, scoring, and filtering. It is also usable as a mail batch language, both for sending and receiving mail. .PP The following options are accepted: .TP .BI \-A \ name Executes an .I account command (see below) for \fIname\fR after the startup files have been read. .TP .BI \-a \ file Attach the given file to the message. .TP .B \-B Make standard input and standard output line-buffered. .TP .BI \-b \ address Send blind carbon copies to list. List should be a comma-separated list of names. .TP .BI \-c \ address Send carbon copies to list of users. .TP .B \-D Start in .I disconnected mode; see the description for the .I disconnected variable option. .TP .B \-d Enables debugging messages and disables the actual delivery of messages. Unlike .IR \-v , this option is intended for .I mailx development only. .TP .B \-e Just check if mail is present in the system mailbox. If yes, return an exit status of zero, else, a non-zero value. .TP .B \-E If an outgoing message does not contain any text in its first or only message part, do not send it but discard it silently, effectively setting the .I skipemptybody variable at program startup. This is useful for sending messages from scripts started by .IR cron (8). .TP \fB\-f\fR [\fIfile\fR] Read in the contents of the user's mbox (or the specified file) for processing; when .I mailx is quit, it writes undeleted messages back to this file. The string \fIfile\fR is handled as described for the .I folder command below. .TP .B \-F Save the message to send in a file named after the local part of the first recipient's address. .TP .B \-H Print header summaries for all messages and exit. .TP \fB\-h\fI hops\fR Invoke sendmail with the specified hop count. This option has no effect when SMTP is used for sending mail. .TP .B \-i Ignore tty interrupt signals. This is particularly useful when using \fImailx\fR on noisy phone lines. .TP .B \-I Shows the `Newsgroup:' or `Article-Id:' fields in the header summary. Only applicable in combination with .IR \-f . .TP .B \-n Inhibits reading /etc/nail.rc upon startup. This option should be activated for .I mailx scripts that are invoked on more than one machine, because the contents of that file may differ between them. .TP .B \-N Inhibits the initial display of message headers when reading mail or editing a mail folder. .TP .BI \-q \ file Start the message with the contents of the specified file. May be given in send mode only. .TP .BI \-r \ address Sets the .I From address. Overrides any .I from variable specified in environment or startup files. Tilde escapes are disabled. The \fI\-r\fI address\fR options are passed to the mail transfer agent unless SMTP is used. This option exists for compatibility only; it is recommended to set the .I from variable directly instead. .TP .B \-R Opens any folders read-only. .TP .BI \-s \ subject Specify subject on command line (only the first argument after the .I \-s flag is used as a subject; be careful to quote subjects containing spaces). .TP \fB\-S\fI\ variable\fR[\fB=\fIvalue\fR] Sets the internal option .I variable and, in case of a string option, assigns .I value to it. .TP .BI \-T \ name Writes the `Message-Id:' and `Article-Id:' header fields of each message read in the file .IR name . Implies .IR \-I . Compressed files are handled as described for the .I folder command below. .TP .B \-t The message to be sent is expected to contain a message header with `To:', `Cc:', or `Bcc:' fields giving its recipients. Recipients specified on the command line are ignored. .TP .BI \-u \ user Reads the mailbox of the given user name. .TP .B \-v Verbose mode. The details of delivery are displayed on the user's terminal. .TP .B \-V Print \fImailx\fR's version and exit. .TP .B \-~ Enable tilde escapes even if not in interactive mode. .SS "Sending mail" To send a message to one or more people, \fImailx\fR can be invoked with arguments which are the names of people to whom the mail will be sent. The user is then expected to type in his message, followed by an `control-D' at the beginning of a line. The section below Replying to or originating mail, describes some features of \fImailx\fR available to help when composing letters. .SS "Reading mail" In normal usage \fImailx\fR is given no arguments and checks the user's mail out of the post office, then prints out a one line header of each message found. The current message is initially the first message (numbered 1) and can be printed using the print command which can be abbreviated `p'). The user can move among the messages much as he moves between lines in .IR ed (1), with the commands `+' and `\-' moving backwards and forwards, and simple numbers. .SS "Disposing of mail" After examining a message the user can delete `d') the message or reply `r') to it. Deletion causes the \fImailx\fR program to forget about the message. This is not irreversible; the message can be undeleted `u') by giving its number, or the \fImailx\fR session can be aborted by giving the exit `x') command. Deleted messages will, however, usually disappear never to be seen again. .SS "Specifying messages" Commands such as print and delete can be given a list of message numbers as arguments to apply to a number of messages at once. Thus `\fIdelete 1 2\fR' deletes messages 1 and 2, while `\fIdelete 1-5\fR' deletes messages 1 through 5. In sorted or threaded mode (see the .I sort and .I thread commands), `\fIdelete 1-5\fR' deletes the messages that are located between (and including) messages 1 through 5 in the sorted/threaded order, as shown in the header summary. The following special message names exist: .TP .B :n All new messages. .TP .B :o All old messages (any not in state read or new). .TP .B :u All unread messages. .TP .B :d All deleted messages (for the .I undelete command). .TP .B :r All read messages. .TP .B :f All `flagged' messages. .TP .B :a All answered messages (cf. the .I markanswered variable). .TP .B :t All messages marked as draft. .TP .B :k All `killed' messages. .TP .B :j All messages classified as junk. .TP .B . The current message. .TP .B ; The message that was previously the current message. .TP .B , The parent message of the current message, that is the message with the Message-ID given in the `In-Reply-To:' field or the last entry of the `References:' field of the current message. .TP .B \- The next previous undeleted message, or the next previous deleted message for the .I undelete command. In sorted/threaded mode, the next previous such message in the sorted/threaded order. .TP .B + The next undeleted message, or the next deleted message for the .I undelete command. In sorted/threaded mode, the next such message in the sorted/threaded order. .TP .B ^ The first undeleted message, or the first deleted message for the .I undelete command. In sorted/threaded mode, the first such message in the sorted/threaded order. .TP .B $ The last message. In sorted/threaded mode, the last message in the sorted/threaded order. .TP .BI & x In threaded mode, selects the message addressed with .IR x , where .I x is any other message specification, and all messages from the thread that begins at it. Otherwise, it is identical to .IR x . If .I x is omitted, the thread beginning with the current message is selected. .TP .B * All messages. .TP .B ` All messages that were included in the message list for the previous command. .TP .BI / string All messages that contain .I string in the subject field (case ignored). See also the .I searchheaders variable. If .I string is empty, the string from the previous specification of that type is used again. .TP .I address All messages from .IR address . By default, this is a case-sensitive search for the complete email address. If the .I allnet variable is set, only the local part of the addresses is evaluated for the comparison. Otherwise if the .I showname variable is set, a case-sensitive search for the complete real name of a sender is performed. The IMAP-style .BI (from\ address) expression can be used instead if substring matches are desired. .TP .BI ( criterion ) All messages that satisfy the given IMAP-style SEARCH .IR criterion . This addressing mode is available with all types of folders; for folders not located on IMAP servers, or for servers unable to execute the SEARCH command, .I mailx will perform the search locally. Strings must be enclosed by double quotes `"' in their entirety if they contain white space or parentheses; within the quotes, only backslash `\e' is recognized as an escape character. All string searches are case-insensitive. When the description indicates that the `envelope' representation of an address field is used, this means that the search string is checked against both a list constructed as .nf .sp \fB("\fIreal name\fB" "\fIsource-route\fB" "\fIlocal-part\fB" "\fIdomain-part\fB")\fR .sp .fi for each address, and the addresses without real names from the respective header field. Criteria can be nested using parentheses. .TP \fB(\fIcriterion1 criterion2\fR .\|.\|. \fIcriterionN\fB)\fR All messages that satisfy all of the given criteria. .TP .BI (or " criterion1 criterion2" ) All messages that satisfy either .I criterion1 or .IR criterion2 , or both. To connect more than two criteria using `or', (or) specifications have to be nested using additional parentheses, as with `(or\ a\ (or\ b\ c))'; `(or\ a\ b\ c)' means ((a or b) and c). For a simple `or' operation of independent criteria on the lowest nesting level, it is possible to achieve similar effects by using three separate criteria, as with `(a)\ (b)\ (c)'. .TP .BI (not " criterion" ) All messages that do not satisfy .IR criterion . .TP .BI (bcc " string" ) All messages that contain .I string in the `envelope' representation of the .I Bcc: field. .TP .BI (cc " string" ) All messages that contain .I string in the `envelope' representation of the .I Cc: field. .TP .BI (from " string" ) All messages that contain .I string in the `envelope' representation of the .I From: field. .TP .BI (subject " string" ) All messages that contain .I string in the .I Subject: field. .TP .BI (to " string" ) All messages that contain .I string in the `envelope' representation of the .I To: field. .TP .BI (header " name string" ) All messages that contain .I string in the specified .I Name: field. .TP .BI (body " string" ) All messages that contain .I string in their body. .TP .BI (text " string" ) All messages that contain .I string in their header or body. .TP .BI (larger " size" ) All messages that are larger than .I size (in bytes). .TP .BI (smaller " size" ) All messages that are smaller than .I size (in bytes). .TP .BI (before " date" ) All messages that were received before .IR date ; .I date must be in the form \fId\fR[\fId\fR]\fB-\fImon\fB-\fIyyyy\fR, where \fId\fR[\fId\fR] is the day of the month as one or two digits, .I mon is the name of the month\(emone of `Jan', `Feb', `Mar', `Apr', `May', `Jun', `Jul', `Aug', `Sep', `Oct', `Nov', or `Dec', and .I yyyy is the year as four digits; e.\|g. "30-Aug-2004". .TP .BI (on " date" ) All messages that were received on the specified date. .TP .BI (since " date" ) All messages that were received since the specified date. .TP .BI (sentbefore " date" ) All messages that were sent on the specified date. .TP .BI (senton " date" ) All messages that were sent on the specified date. .TP .BI (sentsince " date" ) All messages that were sent since the specified date. .TP .B () The same criterion as for the previous search. This specification cannot be used as part of another criterion. If the previous command line contained more than one independent criterion, the last of those criteria is used. .PP A practical method to read a set of messages is to issue a .I from command with the search criteria first to check for appropriate messages, and to read each single message then by typing `\fB`\fR' repeatedly. .SS "Replying to or originating mail" The .I reply command can be used to set up a response to a message, sending it back to the person who it was from. Text the user types in then, up to an end-of-file, defines the contents of the message. While the user is composing a message, \fImailx\fR treats lines beginning with the character `~' specially. For instance, typing `~m' (alone on a line) will place a copy of the current message into the response right shifting it by a tabstop (see .I indentprefix variable, below). Other escapes will set up subject fields, add and delete recipients to the message, attach files to it and allow the user to escape to an editor to revise the message or to a shell to run some commands. (These options are given in the summary below.) .SS "Ending a mail processing session" The user can end a \fImailx\fR session with the quit (`q') command. Messages which have been examined go to the user's mbox file unless they have been deleted in which case they are discarded. Unexamined messages go back to the post office. (See the \-f option above). .SS "Personal and systemwide distribution lists" It is also possible to create a personal distribution lists so that, for instance, the user can send mail to `\fIcohorts\fR' and have it go to a group of people. Such lists can be defined by placing a line like .nf \fBalias\fI cohorts bill ozalp jkf mark kridle@ucbcory\fR .fi in the file .mailrc in the user's home directory. The current list of such aliases can be displayed with the alias command in \fImailx\fR. System wide distribution lists can be created by editing /etc/aliases, see .IR aliases (5) and .IR sendmail (8); these are kept in a different syntax. In mail the user sends, personal aliases will be expanded in mail sent to others so that they will be able to reply to the recipients. System wide aliases are not expanded when the mail is sent, but any reply returned to the machine will have the system wide alias expanded as all mail goes through sendmail. .SS "Recipient address specifications" When an address is used to name a recipient (in any of To, Cc, or Bcc), names of local mail folders and pipes to external commands can also be specified; the message text is then written to them. The rules are: Any name which starts with a .RB ` | ' character specifies a pipe, the command string following the `|' is executed and the message is sent to its standard input; any other name which contains a .RB ` @ ' character is treated as a mail address; any other name which starts with a .RB ` + ' character specifies a folder name; any other name which contains a .RB ` / ' character but no .RB ` ! ' or .RB ` % ' character before also specifies a folder name; what remains is treated as a mail address. Compressed folders are handled as described for the .I folder command below. .SS "Network mail (Internet / ARPA, UUCP, Berknet)" See .IR mailaddr (7) for a description of network addresses. \fIMailx\fR has a number of options which can be set in the .mailrc file to alter its behavior; thus `\fIset askcc\fR' enables the askcc feature. (These options are summarized below). .SS "MIME types" For any outgoing attachment, \fImailx\fR tries to determine the content type. It does this by reading MIME type files whose lines have the following syntax: .nf \fItype\fB/\fIsubtype extension \fR[\fIextension \fR.\ .\ .]\fR .fi where type/subtype are strings describing the file contents, and extension is the part of a filename starting after the last dot. Any line not immediately beginning with an ASCII alphabetical character is ignored by \fImailx\fR. If there is a match with the extension of the file to attach, the given type/subtype pair is used. Otherwise, or if the filename has no extension, the content types text/plain or application/octet-stream are used, the first for text or international text files, the second for any file that contains formatting characters other than newlines and horizontal tabulators. .SS "Character sets" .I Mailx normally detects the character set of the terminal using the LC_CTYPE locale setting. If the locale cannot be used appropriately, the \fIttycharset\fR variable should be set to provide an explicit value. When reading messages, their text is converted to the terminal character set if possible. Unprintable characters and illegal byte sequences are detected and replaced by Unicode substitute characters or question marks unless the .I print-all-chars is set at initialization time. .PP The character set for outgoing messages is not necessarily the same as the one used on the terminal. If an outgoing text message contains characters not representable in US-ASCII, the character set being used must be declared within its header. Permissible values can be declared using the \fIsendcharsets\fR variable, separated by commas; .I mailx tries each of the values in order and uses the first appropriate one. If the message contains characters that cannot be represented in any of the given character sets, the message will not be sent, and its text will be saved to the `dead.letter' file. Messages that contain NUL bytes are not converted. .PP Outgoing attachments are converted if they are plain text. If the .I sendcharsets variable contains more than one character set name, the .I ~@ tilde escape will ask for the character sets for individual attachments if it is invoked without arguments. .PP Best results are usually achieved when .I mailx is run in a UTF-8 locale on a UTF-8 capable terminal. In this setup, characters from various countries can be displayed, while it is still possible to use more simple character sets for sending to retain maximum compatibility with older mail clients. .SS "Commands" Each command is typed on a line by itself, and may take arguments following the command word. The command need not be typed in its entirety \(en the first command which matches the typed prefix is used. For commands which take message lists as arguments, if no message list is given, then the next message forward which satisfies the command's requirements is used. If there are no messages forward of the current message, the search proceeds backwards, and if there are no good messages at all, \fImailx\fR types `\fIapplicable messages\fR' and aborts the command. If the command begins with a \fI#\fR sign, the line is ignored. .PP The arguments to commands can be quoted, using the following methods: .IP \(bu An argument can be enclosed between paired double-quotes "\|" or single-quotes '\|'; any white space, shell word expansion, or backslash characters within the quotes are treated literally as part of the argument. A double-quote will be treated literally within single-quotes and vice versa. These special properties of the quote marks occur only when they are paired at the beginning and end of the argument. .IP \(bu A backslash outside of the enclosing quotes is discarded and the following character is treated literally as part of the argument. .IP \(bu An unquoted backslash at the end of a command line is discarded and the next line continues the command. .PP Filenames, where expected, are subjected to the following transformations, in sequence: .IP \(bu If the filename begins with an unquoted plus sign, and the .I folder variable is defined, the plus sign will be replaced by the value of the .I folder variable followed by a slash. If the .I folder variable is unset or is set to null, the filename will be unchanged. .IP \(bu Shell word expansions are applied to the filename. If more than a single pathname results from this expansion and the command is expecting one file, an error results. .PP The following commands are provided: .TP .B \- Print out the preceding message. If given a numeric argument n, goes to the n'th previous message and prints it. .TP .B ? Prints a brief summary of commands. .TP .B ! Executes the shell (see .IR sh (1) and .IR csh (1)) command which follows. .TP .B | A synonym for the \fIpipe\fR command. .TP .B account (ac) Creates, selects or lists an email account. An account is formed by a group of commands, primarily of those to set variables. With two arguments, of which the second is a `{', the first argument gives an account name, and the following lines create a group of commands for that account until a line containing a single `}' appears. With one argument, the previously created group of commands for the account name is executed, and a .I folder command is executed for the system mailbox or inbox of that account. Without arguments, the list of accounts and their contents are printed. As an example, .sp .nf \fBaccount\fI myisp\fR \fB{\fR set folder=imaps://mylogin@imap.myisp.example set record=+Sent set from="myname@myisp.example (My Name)" set smtp=smtp.myisp.example \fB}\fR .fi .sp creates an account named `myisp' which can later be selected by specifying `account myisp'. .TP .B alias (a) With no arguments, prints out all currently-defined aliases. With one argument, prints out that alias. With more than one argument, creates a new alias or changes an old one. .TP .B alternates (alt) The alternates command is useful if the user has accounts on several machines. It can be used to inform \fImailx\fR that the listed addresses all belong to the invoking user. When he replies to messages, \fImailx\fR will not send a copy of the message to any of the addresses listed on the alternates list. If the alternates command is given with no argument, the current set of alternate names is displayed. .TP .B answered (ans) Takes a message list and marks each message as a having been answered. This mark has no technical meaning in the mail system; it just causes messages to be marked in the header summary, and makes them specially addressable. .TP .B cache Only applicable to cached IMAP mailboxes; takes a message list and reads the specified messages into the IMAP cache. .TP .B call Calls a macro (see the .I define command). .TP .B cd Same as chdir. .TP .B certsave Only applicable to S/MIME signed messages. Takes a message list and a file name and saves the certificates contained within the message signatures to the named file in both human-readable and PEM format. The certificates can later be used to send encrypted messages to the messages' originators by setting the .I smime-encrypt-user@host variable. .TP .B chdir (ch) Changes the user's working directory to that specified, if given. If no directory is given, then changes to the user's login directory. .TP .B classify (cl) Takes a list of messages and examines their contents for characteristics of junk mail using Bayesian filtering. Messages considered to be junk are then marked as such. The junk mail database is not changed. .TP .B collapse (coll) Only applicable to threaded mode. Takes a message list and makes all replies to these messages invisible in header summaries, unless they are in state `new'. .TP .B connect (conn) If operating in disconnected mode on an IMAP mailbox, switch to online mode and connect to the mail server while retaining the mailbox status. See the description of the .I disconnected variable for more information. .TP .B copy (c) The copy command does the same thing that .I save does, except that it does not mark the messages it is used on for deletion when the user quits. Compressed files and IMAP mailboxes are handled as described for the .I folder command. .TP .B Copy (C) Similar to .IR copy , but saves the messages in a file named after the local part of the sender address of the first message. .TP .B decrypt (dec) For unencrypted messages, this command is identical to .IR copy . Encrypted messages are first decrypted, if possible, and then copied. .TP .B Decrypt (Dec) Similar to .IR decrypt , but saves the messages in a file named after the local part of the sender address of the first message. .TP .B define (def) Defines a macro. A macro definition is a sequence of commands in the following form: .sp .nf \fBdefine\fR \fIname\fB {\fR \fIcommand1\fR \fIcommand2\fR .\|.\|. \fIcommandN\fR \fB}\fR .fi .sp Once defined, a macro can be explicitly invoked using the .I call command, or can be implicitly invoked by setting the .I folder-hook or .I folder-hook-fullname variables. .TP .B defines Prints the currently defined macros including their contents. .TP .B delete (d) Takes a list of messages as argument and marks them all as deleted. Deleted messages will not be saved in mbox, nor will they be available for most other commands. .TP .B discard Same as ignore. .TP .B disconnect (disco) If operating in online mode on an IMAP mailbox, switch to disconnected mode while retaining the mailbox status. See the description of the .I disconnected variable for more information. A list of messages may optionally be given as argument; the respective messages are then read into the cache before the connection is closed. Thus `disco *' makes the entire current mailbox available for disconnected use. .TP .BR dp \ or \ dt Deletes the current message and prints the next message. If there is no next message, \fImailx\fR says `\fIat EOF\fR'. .TP .B draft Takes a message list and marks each message as a draft. This mark has no technical meaning in the mail system; it just causes messages to be marked in the header summary, and makes them specially addressable. .TP .B echo Echoes its arguments, resolving special names as documented for the folder command. The escape sequences `\fB\ea\fR', `\fB\eb\fR', `\fB\ec\fR', `\fB\ef\fR', `\fB\en\fR', `\fB\er\fR', `\fB\et\fR', `\fB\ev\fR', `\fB\e\e\fR', and `\fB\e0\fInum\fR' are interpreted as with the .IR echo (1) command. .TP .B edit (e) Takes a list of messages and points the text editor at each one in turn. Modified contents are discarded unless the .I writebackedited variable is set. .TP .B else Marks the end of the then-part of an if statement and the beginning of the part to take effect if the condition of the if statement is false. .TP .B endif Marks the end of an if statement. .TP .B exit (ex or x) Effects an immediate return to the Shell without modifying the user's system mailbox, his mbox file, or his edit file in \-f. .TP .B file (fi) The same as folder. .TP .B flag (fl) Takes a message list and marks the messages as `flagged' for urgent/special attention. This mark has no technical meaning in the mail system; it just causes messages to be highlighted in the header summary, and makes them specially addressable. .TP .B folders With no arguments, list the names of the folders in the folder directory. With an existing folder as an argument, lists then names of folders below the named folder; e.\|g. the command `folders @' lists the folders on the base level of the current IMAP server. See also the .I imap-list-depth variable. .TP .B folder (fold) The folder command switches to a new mail file or folder. With no arguments, it tells the user which file he is currently reading. If an argument is given, it will write out changes (such as deletions) the user has made in the current file and read in the new file. Some special conventions are recognized for the name. \fB#\fR means the previous file, \fB%\fR means the invoking user's system mailbox, \fB%\fIuser\fR means \fIuser's\fR system mailbox, \fB&\fR means the invoking user's mbox file, and \fB+\fIfile\fI means a \fIfile\fR in the folder directory. \fB%:\fIfilespec\fR expands to the same value as \fIfilespec\fR, but the file is handled as a system mailbox e.\ g. by the mbox and save commands. If the name matches one of the strings defined with the .I shortcut command, it is replaced by its long form and expanded. If the name ends with \fB.gz\fR or \fB.bz2\fR, it is treated as compressed with .IR gzip (1) or .IR bzip2 (1), respectively. Likewise, if \fIname\fR does not exist, but either \fIname\fB.gz\fR or \fIname\fB.bz2\fR exists, the compressed file is used. If \fIname\fR refers to a directory with the subdirectories `tmp', `new', and `cur', it is treated as a folder in .I maildir format. A name of the form .nf \fIprotocol\fB://\fR[\fIuser\fB@\fR]\fIhost\fR[\fB:\fIport\fR][\fB/\fIfile\fR] .fi is taken as an Internet mailbox specification. The supported protocols are currently .B imap (IMAP v4r1), .B imaps (IMAP with SSL/TLS encryption), .B pop3 (POP3), and .B pop3s (POP3 with SSL/TLS encryption). If .I user contains special characters, in particular `/' or `%', they must be escaped in URL notation, as `%2F' or `%25'. The optional .I file part applies to IMAP only; if it is omitted, the default `INBOX' is used. If \fImailx\fR is connected to an IMAP server, a name of the form \fB@\fImailbox\fR refers to the \fImailbox\fR on that server. If the `folder' variable refers to an IMAP account, the special name `%' selects the `INBOX' on that account. .TP .B Followup (F) Similar to .IR Respond , but saves the message in a file named after the local part of the first recipient's address. .TP .B followup (fo) Similar to .IR respond , but saves the message in a file named after the local part of the first recipient's address. .TP .B followupall Similar to .IR followup , but responds to all recipients regardless of the .I flipr and .I Replyall variables. .TP .B followupsender Similar to .IR Followup , but responds to the sender only regardless of the .I flipr and .I Replyall variables. .TP .B forward (fwd) Takes a message and the address of a recipient and forwards the message to him. The text of the original message is included in the new one, with the value of the .I fwdheading variable printed before. The .I fwdignore and .I fwdretain commands specify which header fields are included in the new message. Only the first part of a multipart message is included unless the .I forward-as-attachment option is set. .TP .B Forward (Fwd) Similar to .IR forward , but saves the message in a file named after the local part of the recipient's address. .TP .B from (f) Takes a list of messages and prints their message headers, piped through the pager if the output does not fit on the screen. .TP .B fwdignore Specifies which header fields are to be ignored with the .I forward command. This command has no effect when the .I forward-as-attachment option is set. .TP .B fwdretain Specifies which header fields are to be retained with the .I forward command. .I fwdretain overrides .IR fwdignore . This command has no effect when the .I forward-as-attachment option is set. .TP .B good (go) Takes a list of messages and marks all of them as not being junk mail. Data from these messages is then inserted into the junk mail database for future classification. .TP .B headers (h) Lists the current range of headers, which is an 18-message group. If a `+' argument is given, then the next 18-message group is printed, and if a `\-' argument is given, the previous 18-message group is printed. .TP .B help A synonym for ?. .TP .B hold (ho, also preserve) Takes a message list and marks each message therein to be saved in the user's system mailbox instead of in mbox. Does not override the delete command. .I mailx deviates from the POSIX standard with this command, as a `next' command issued after `hold' will display the following message, not the current one. .TP .B if Commands in \fImailx\fR's startup files can be executed conditionally depending on whether the user is sending or receiving mail with the if command. For example: .nf \fBif \fIreceive\fR \fIcommands .\ .\ .\fR \fBendif\fR .fi An else form is also available: .nf \fBif \fIreceive\fR \fIcommands .\ .\ .\fR \fBelse\fR \fIcommands .\ .\ .\fR \fBendif\fR .fi Note that the only allowed conditions are .BR receive , .BR send , and .B term (execute command if standard input is a tty). .TP .B ignore Add the list of header fields named to the ignored list. Header fields in the ignore list are not printed on the terminal when a message is printed. This command is very handy for suppression of certain machine-generated header fields. The Type and Print commands can be used to print a message in its entirety, including ignored fields. If ignore is executed with no arguments, it lists the current set of ignored fields. .TP .B imap Sends command strings directly to the current IMAP server. \fIMailx\fR operates always in IMAP \fIselected state\fR on the current mailbox; commands that change this will produce undesirable results and should be avoided. Useful IMAP commands are: .RS .TP .B create Takes the name of an IMAP mailbox as an argument and creates it. .TP .B getquotaroot .\" RFC 2087 Takes the name of an IMAP mailbox as an argument and prints the quotas that apply to the mailbox. Not all IMAP servers support this command. .TP .B namespace .\" RFC 2342 Takes no arguments and prints the Personal Namespaces, the Other User's Namespaces, and the Shared Namespaces. Each namespace type is printed in parentheses; if there are multiple namespaces of the same type, inner parentheses separate them. For each namespace, a namespace prefix and a hierarchy separator is listed. Not all IMAP servers support this command. .RE .TP .B inc Same as .IR newmail . .TP .B junk (j) Takes a list of messages and marks all of them as junk mail. Data from these messages is then inserted into the junk mail database for future classification. .TP .B kill (k) Takes a list of messages and `kills' them. Killed messages are not printed in header summaries, and are ignored by the .I next command. The .I kill command also sets the score of the messages to negative infinity, so that subsequent .I score commands will not unkill them again. Killing is only effective for the current session on a folder; when it is quit, all messages are automatically unkilled. .TP .B list Prints the names of all available commands. .TP .B Mail (M) Similar to .IR mail , but saves the message in a file named after the local part of the first recipient's address. .TP .B mail (m) Takes as argument login names and distribution group names and sends mail to those people. .TP .B mbox Indicate that a list of messages be sent to mbox in the user's home directory when .I mailx is quit. This is the default action for messages if unless the .I hold option is set. .I mailx deviates from the POSIX standard with this command, as a `next' command issued after `mbox' will display the following message, not the current one. .TP .B move (mv) Acts like .IR copy , but marks the messages for deletion if they were transferred successfully. .TP .B Move (Mv) Similar to .IR move , but moves the messages to a file named after the local part of the sender address of the first message. .TP .B newmail Checks for new mail in the current folder without committing any changes before. If new mail is present, a message is printed. If the .I header variable is set, the headers of each new message are also printed. .TP .B next (n) like + or CR) Goes to the next message in sequence and types it. With an argument list, types the next matching message. .TP .B New Same as .IR unread . .TP .B new Same as .IR unread . .TP .B online Same as .IR connect . .TP .B noop If the current folder is located on an IMAP or POP3 server, a NOOP command is sent. Otherwise, no operation is performed. .TP .B Pipe (Pi) Like .I pipe but also pipes ignored header fields and all parts of MIME .I multipart/alternative messages. .TP .B pipe (pi) Takes a message list and a shell command and pipes the messages through the command. Without an argument, the current message is piped through the command given by the \fIcmd\fR variable. If the \fI page\fR variable is set, every message is followed by a formfeed character. .TP .B preserve (pre) A synonym for .IR hold . .TP .B Print (P) Like .I print but also prints out ignored header fields and all parts of MIME .I multipart/alternative messages. See also .IR print , .IR ignore , and .IR retain . .TP .B print (p) Takes a message list and types out each message on the user's terminal. If the message is a MIME multipart message, all parts with a content type of `text' or `message' are shown, the other are hidden except for their headers. Messages are decrypted and converted to the terminal character set if necessary. .TP .B probability (prob) For each word given as argument, the contents of its junk mail database entry are printed. .TP .B quit (q) Terminates the session, saving all undeleted, unsaved messages in the user's mbox file in his login directory, preserving all messages marked with hold or preserve or never referenced in his system mailbox, and removing all other messages from his system mailbox. If new mail has arrived during the session, the message `\fIYou have new mail\fR' is given. If given while editing a mailbox file with the \-f flag, then the edit file is rewritten. A return to the Shell is effected, unless the rewrite of edit file fails, in which case the user can escape with the exit command. .TP .B redirect (red) Same as .IR resend . .TP .B Redirect (Red) Same as .IR Resend . .TP .B remove (rem) Removes the named folders. The user is asked for confirmation in interactive mode. .TP .B rename (ren) Takes the name of an existing folder and the name for the new folder and renames the first to the second one. Both folders must be of the same type and must be located on the current server for IMAP. .TP .B Reply (R) Reply to originator. Does not reply to other recipients of the original message. .TP .BR reply (r) Takes a message list and sends mail to the sender and all recipients of the specified message. The default message must not be deleted. .TP .B replyall Similar to .IR reply , but responds to all recipients regardless of the .I flipr and .I Replyall variables. .TP .B replysender Similar to .IR Reply , but responds to the sender only regardless of the .I flipr and .I Replyall variables. .TP .B Resend Like .IR resend , but does not add any header lines. This is not a way to hide the sender's identity, but useful for sending a message again to the same recipients. .TP .B resend Takes a list of messages and a user name and sends each message to the named user. `Resent-From:' and related header fields are prepended to the new copy of the message. .TP .B Respond Same as .IR Reply . .TP .B respond Same as .IR reply . .TP .B respondall Same as .IR replyall . .TP .B respondsender Same as .IR replysender . .TP .B retain Add the list of header fields named to the retained list. Only the header fields in the retain list are shown on the terminal when a message is printed. All other header fields are suppressed. The Type and Print commands can be used to print a message in its entirety. If retain is executed with no arguments, it lists the current set of retained fields. .TP .B Save (S) Similar to .IR save , but saves the messages in a file named after the local part of the sender of the first message instead of taking a filename argument. .TP .B save (s) Takes a message list and a filename and appends each message in turn to the end of the file. If no filename is given, the mbox file is used. The filename in quotes, followed by the line count and character count is echoed on the user's terminal. If editing a system mailbox, the messages are marked for deletion. Compressed files and IMAP mailboxes are handled as described for the .I \-f command line option above. .TP .B savediscard Same as saveignore. .TP .B saveignore Saveignore is to save what ignore is to print and type. Header fields thus marked are filtered out when saving a message by save or when automatically saving to mbox. This command should only be applied to header fields that do not contain information needed to decode the message, as MIME content fields do. If saving messages on an IMAP account, ignoring fields makes it impossible to copy the data directly on the server, thus operation usually becomes much slower. .TP .B saveretain Saveretain is to save what retain is to print and type. Header fields thus marked are the only ones saved with a message when saving by save or when automatically saving to mbox. Saveretain overrides saveignore. The use of this command is strongly discouraged since it may strip header fields that are needed to decode the message correctly. .TP .B score (sc) Takes a message list and a floating point number and adds the number to the score of each given message. All messages start at score 0 when a folder is opened. When the score of a message becomes negative, it is `killed' with the effects described for the .I kill command; otherwise if it was negative before and becomes positive, it is `unkilled'. Scores only refer to the currently opened instance of a folder. .TP .B set (se) With no arguments, prints all variable values, piped through the pager if the output does not fit on the screen. Otherwise, sets option. Arguments are of the form option=value (no space before or after =) or option. Quotation marks may be placed around any part of the assignment statement to quote blanks or tabs, i.\|e. `\fIset indentprefix="\->"\fR'. If an argument begins with .BR no , as in `\fBset no\fIsave\fR', the effect is the same as invoking the .I unset command with the remaining part of the variable (`\fIunset \fIsave\fR'). .TP .B seen Takes a message list and marks all messages as having been read. .TP .B shell (sh) Invokes an interactive version of the shell. .TP .B shortcut Defines a shortcut name and its string for expansion, as described for the .I folder command. With no arguments, a list of defined shortcuts is printed. .TP .B show (Sh) Like .IR print , but performs neither MIME decoding nor decryption so that the raw message text is shown. .TP .B size Takes a message list and prints out the size in characters of each message. .TP .B sort Create a sorted representation of the current folder, and change the .I next command and the addressing modes such that they refer to messages in the sorted order. Message numbers are the same as in regular mode. If the .I header variable is set, a header summary in the new order is also printed. Possible sorting criteria are: .RS .TP .B date Sort the messages by their `Date:' field, that is by the time they were sent. .TP .B from Sort messages by the value of their `From:' field, that is by the address of the sender. If the .I showname variable is set, the sender's real name (if any) is used. .TP .B size Sort the messages by their size. .TP .B score Sort the messages by their score. .TP .B status Sort the messages by their message status (new, read, old, etc.). .TP .B subject Sort the messages by their subject. .TP .B thread Create a threaded order, as with the .I thread command. .TP .B to Sort messages by the value of their `To:' field, that is by the address of the recipient. If the .I showname variable is set, the recipient's real name (if any) is used. .RE .IP If no argument is given, the current sorting criterion is printed. .TP .B source The source command reads commands from a file. .TP .B thread (th) Create a threaded representation of the current folder, i.\|e. indent messages that are replies to other messages in the header display, and change the .I next command and the addressing modes such that they refer to messages in the threaded order. Message numbers are the same as in unthreaded mode. If the .I header variable is set, a header summary in threaded order is also printed. .TP .B top Takes a message list and prints the top few lines of each. The number of lines printed is controlled by the variable toplines and defaults to five. .TP .B touch Takes a message list and marks the messages for saving in the .I mbox file. .I mailx deviates from the POSIX standard with this command, as a `next' command issued after `mbox' will display the following message, not the current one. .TP .B Type (T) Identical to the Print command. .TP .B type (t) A synonym for print. .TP .B unalias Takes a list of names defined by alias commands and discards the remembered groups of users. The group names no longer have any significance. .TP .B unanswered Takes a message list and marks each message as not having been answered. .TP .B uncollapse (unc) Only applicable to threaded mode. Takes a message list and makes the message and all replies to it visible in header summaries again. When a message becomes the current message, it is automatically made visible. Also when a message with collapsed replies is printed, all of these are automatically uncollapsed. .TP .B undef Undefines each of the named macros. It is not an error to use a name that does not belong to one of the currently defined macros. .TP .B undelete (u) Takes a message list and marks each message as not being deleted. .TP .B undraft Takes a message list and marks each message as a draft. .TP .B unflag Takes a message list and marks each message as not being `flagged'. .TP .B unfwdignore Removes the header field names from the list of ignored fields for the .I forward command. .TP .B unfwdretain Removes the header field names from the list of retained fields for the .I forward command. .TP .B ungood Takes a message list and undoes the effect of a .I good command that was previously applied on exactly these messages. .TP .B unignore Removes the header field names from the list of ignored fields. .TP .B unjunk Takes a message list and undoes the effect of a .I junk command that was previously applied on exactly these messages. .TP .B unkill Takes a message list and `unkills' each message. Also sets the score of the messages to 0. .TP .B Unread Same as .IR unread . .TP .B unread (U) Takes a message list and marks each message as not having been read. .TP .B unretain Removes the header field names from the list of retained fields. .TP .B unsaveignore Removes the header field names from the list of ignored fields for saving. .TP .B unsaveretain Removes the header field names from the list of retained fields for saving. .TP .B unset Takes a list of option names and discards their remembered values; the inverse of set. .TP .B unshortcut Deletes the shortcut names given as arguments. .TP .B unsort Disable sorted or threaded mode (see the .I sort and .I thread commands), return to normal message order and, if the .I header variable is set, print a header summary. .TP .B unthread (unth) Same as .IR unsort . .TP .B verify (verif) Takes a message list and verifies each message. If a message is not an S/MIME signed message, verification will fail for it. The verification process checks if the message was signed using a valid certificate, if the message sender's email address matches one of those contained within the certificate, and if the message content has been altered. .TP .B visual (v) Takes a message list and invokes the display editor on each message. Modified contents are discarded unless the .I writebackedited variable is set. .TP .B write (w) For conventional messages, the body without all headers is written. The output is decrypted and converted to its native format, if necessary. If the output file exists, the text is appended.\(emIf a message is in MIME multipart format, its first part is written to the specified file as for conventional messages, and the user is asked for a filename to save each other part; if the contents of the first part are not to be saved, `write /dev/null' can be used. For the second and subsequent parts, if the filename given starts with a `|' character, the part is piped through the remainder of the filename interpreted as a shell command. In non-interactive mode, only the parts of the multipart message that have a filename given in the part header are written, the other are discarded. The original message is never marked for deletion in the originating mail folder. For attachments, the contents of the destination file are overwritten if the file previously existed. No special handling of compressed files is performed. .TP .B xit (x) A synonym for exit. .TP .B z \fIMailx\fR presents message headers in windowfuls as described under the headers command. The z command scrolls to the next window of messages. If an argument is given, it specifies the window to use. A number prefixed by `+' or `\-' indicates that the window is calculated in relation to the current position. A number without a prefix specifies an absolute window number, and a `$' lets \fImailx\fR scroll to the last window of messages. .TP .B Z Similar to .IR z , but scrolls to the next or previous window that contains at least one new or `flagged' message. .SS "Tilde escapes" Here is a summary of the tilde escapes, which are used when composing messages to perform special functions. Tilde escapes are only recognized at the beginning of lines. The name `\fItilde escape\fR' is somewhat of a misnomer since the actual escape character can be set by the option escape. .TP .BI ~! command Execute the indicated shell command, then return to the message. .TP .B ~. Same effect as typing the end-of-file character. .TP .BI ~< filename Identical to ~r. .TP .BI ~ A `>' for the current message, otherwise ` '. %< A `<' for the current message, otherwise ` '. %% A `%' character. .TE .in -4m .IP The default is `%>\&%a\&%m\ %18f\ %16d\ %4l/%\-5o\ %i%s', or `%>\&%a\&%m\ %20f\ \ %16d\ %3l/%\-5o\ %i%S' if .I bsdcompat is set. .TP .B hostname Use this string as hostname when expanding local addresses instead of the value obtained from .IR uname (2) and .IR getaddrinfo (3). .TP .B imap-auth Sets the IMAP authentication method. Valid values are `login' for the usual password-based authentication (the default), `cram-md5', which is a password-based authentication that does not send the password over the network in clear text, and `gssapi' for GSSAPI-based authentication. .TP \fBimap-auth-\fIuser\fB@\fIhost\fR Sets the IMAP authentication method for a specific account. .TP .B imap-cache Enables caching of IMAP mailboxes. The value of this variable must point to a directory that is either existent or can be created by .IR mailx . All contents of the cache can be deleted by .I mailx at any time; it is not safe to make assumptions about them. .TP .B imap-keepalive IMAP servers may close the connection after a period of inactivity; the standard requires this to be at least 30 minutes, but practical experience may vary. Setting this variable to a numeric .I value greater than 0 causes a NOOP command to be sent each .I value seconds if no other operation is performed. .TP .B imap-list-depth When retrieving the list of folders on an IMAP server, the .I folders command stops after it has reached a certain depth to avoid possible infinite loops. The value of this variable sets the maximum depth allowed. The default is 2. If the folder separator on the current IMAP server is a slash `/', this variable has no effect, and the .I folders command does not descend to subfolders. .TP .B indentprefix String used by the `\fI~m\fR' and `\fI~M\fR' tilde escapes and by the \fIquote\fR option for indenting messages, in place of the normal tab character (^I). Be sure to quote the value if it contains spaces or tabs. .TP .B junkdb The location of the junk mail database. The string is treated like a folder name, as described for the .I folder command. .IP The files in the junk mail database are normally stored in .IR compress (1) format for saving space. If processing time is considered more important, .IR uncompress (1) can be used to store them in plain form. .I Mailx will then work using the uncompressed files. .TP .B LISTER Pathname of the directory lister to use in the .I folders command when operating on local mailboxes. Default is /bin/ls. .TP .B MAIL Is used as the user's mailbox, if set. Otherwise, a system-dependent default is used. Can be a \fIprotocol\fB://\fR string (see the .I folder command for more information). .TP .B MAILX_HEAD A string to put at the beginning of each new message. The escape sequences `\fB\et\fR' (tabulator) and `\fB\en\fR' (newline) are understood. .TP .B MAILX_TAIL A string to put at the end of each new message. The escape sequences `\fB\et\fR' (tabulator) and `\fB\en\fR' (newline) are understood. .TP .B maximum-unencoded-line-length Messages that contain lines longer than the value of this variable are encoded in quoted-printable even if they contain only ASCII characters. The maximum effective value is 950. If set to 0, all ASCII text messages are encoded in quoted-printable. S/MIME signed messages are always encoded in quoted-printable regardless of the value of this variable. .TP .B MBOX The name of the mbox file. It can be the name of a folder. The default is `\fImbox\fR' in the user's home directory. .TP .B NAIL_EXTRA_RC The name of an optional startup file to be read after ~/.mailrc. This variable is ignored if it is imported from the environment; it has an effect only if it is set in /etc/nail.rc or ~/.mailrc to allow bypassing the configuration with e. g. `MAILRC=/dev/null'. Use this file for commands that are not understood by other mailx implementations. .TP .B newfolders If this variable has the value .BR maildir , newly created local folders will be in .I maildir format. .TP .B nss-config-dir A directory that contains the files .RI cert N .db to retrieve certificates, .RI key N .db to retrieve private keys, and secmod.db, where .I N is a digit. These are usually taken from Mozilla installations, so an appropriate value might be `~/.mozilla/firefox/default.clm'. .I Mailx opens these files read-only and does not modify them. However, if the files are modified by Mozilla while .I mailx is running, it will print a `Bad database' message. It may be necessary to create copies of these files that are exclusively used by .I mailx then. Only applicable if S/MIME and SSL/TLS support is built using Network Security Services (NSS). .TP .B ORGANIZATION The value to put into the \fI`Organization:'\fR field of the message header. .TP .B PAGER Pathname of the program to use in the more command or when crt variable is set. The default paginator .IR pg (1) or, in BSD compatibility mode, .IR more (1) is used if this option is not defined. .TP \fBpassword-\fIuser\fB@\fIhost\fR Set the password for .I user when connecting to .IR host . If no such variable is defined for a host, the user will be asked for a password on standard input. Specifying passwords in a startup file is generally a security risk, the file should be readable by the invoking user only. .TP .BI pipe- content/subcontent When a MIME message part of .I content/subcontent type is displayed or it is replied to, its text is filtered through the value of this variable interpreted as a shell command. Special care must be taken when using such commands as mail viruses may be distributed by this method; if messages of type .I application/x-sh were filtered through the shell, for example, a message sender could easily execute arbitrary code on the system .I mailx is running on. .TP .B pop3-keepalive POP3 servers may close the connection after a period of inactivity; the standard requires this to be at least 10 minutes, but practical experience may vary. Setting this variable to a numeric .I value greater than 0 causes a NOOP command to be sent each .I value seconds if no other operation is performed. .TP .B prompt The string printed when a command is accepted. Defaults to `\fB?\ \fR', or to `\fB&\ \fR' if the .I bsdcompat variable is set. .TP .B quote If set, \fImailx\fR starts a replying message with the original message prefixed by the value of the variable \fIindentprefix\fR. Normally, a heading consisting of `Fromheaderfield wrote:' is printed before the quotation. If the string \fInoheading\fR is assigned to the \fIquote\fR variable, this heading is omitted. If the string \fIheaders\fR is assigned, the headers selected by the ignore/retain commands are printed above the message body, thus \fIquote\fR acts like an automatic ~m command then. If the string \fIallheaders\fR is assigned, all headers are printed above the message body, and all MIME parts are included, thus \fIquote\fR acts like an automatic ~M command then. .TP .B record If defined, gives the pathname of the folder used to record all outgoing mail. If not defined, then outgoing mail is not so saved. When saving to this folder fails, the message is not sent but saved to the `dead.letter' file instead. .TP .B replyto A list of addresses to put into the \fI`Reply-To:'\fR field of the message header. If replying to a message, such addresses are handled as if they were in the alternates list. .TP .B screen When \fImailx\fR initially prints the message headers, it determines the number to print by looking at the speed of the terminal. The faster the terminal, the more it prints. This option overrides this calculation and specifies how many message headers are printed. This number is also used for scrolling with the z command. .TP .B sendcharsets A comma-separated list of character set names that can be used in Internet mail. When a message that contains characters not representable in US-ASCII is prepared for sending, .I mailx tries to convert its text to each of the given character sets in order and uses the first appropriate one. The default is `utf-8'. .IP Character sets assigned to this variable should be ordered in ascending complexity. That is, the list should start with e.\|g. `iso-8859-1' for compatibility with older mail clients, might contain some other language-specific character sets, and should end with `utf-8' to handle messages that combine texts in multiple languages. .TP .B sender An address that is put into the `Sender:' field of outgoing messages. This field needs not normally be present. It is, however, required if the `From:' field contains more than one address. It can also be used to indicate that a message was sent on behalf of somebody other; in this case, `From:' should contain the address of the person that took responsibility for the message, and `Sender:' should contain the address of the person that actually sent the message. The .I sender address is handled as if it were in the .I alternates list. .TP .B sendmail To use an alternate mail delivery system, set this option to the full pathname of the program to use. This should be used with care. .TP .B SHELL Pathname of the shell to use in the ! command and the ~! escape. A default shell is used if this option is not defined. .TP .TP .B Sign A string for use with the .I ~A command. .TP .B sign A string for use with the .I ~a command. .TP .B signature Must correspond to the name of a readable file if set. The file's content is then appended to each singlepart message and to the first part of each multipart message. Be warned that there is no possibility to edit the signature for an individual message. .TP .B smime-ca-dir Specifies a directory with CA certificates for verification of S/MIME signed messages. The format is the same as described in .IR SSL_CTX_load_verify_locations (3). Only applicable if S/MIME support is built using OpenSSL. .TP .B smime-ca-file Specifies a file with CA certificates for verification of S/MIME signed messages. The format is the same as described in .IR SSL_CTX_load_verify_locations (3). Only applicable if S/MIME support is built using OpenSSL. .TP \fBsmime-cipher-\fIuser@host\fR Specifies a cipher to use when generating S/MIME encrypted messages for .IR user@host . Valid ciphers are .B rc2-40 (RC2 with 40 bits), .B rc2-64 (RC2 with 64 bits), .B des (DES, 56 bits) and .B des-ede3 (3DES, 112/168 bits). The default is 3DES. It is not recommended to use the other ciphers unless a recipient's client is actually unable to handle 3DES since they are comparatively weak; but even so, the recipient should upgrade his software in preference. .TP .B smime-crl-file Specifies a file that contains a CRL in PEM format to use when verifying S/MIME messages. Only applicable if S/MIME support is built using OpenSSL. .TP .B smime-crl-dir Specifies a directory that contains files with CRLs in PEM format to use when verifying S/MIME messages. Only applicable if S/MIME support is built using OpenSSL. .TP \fBsmime-encrypt-\fIuser@host\fR If this variable is set, messages to .I user@host are encrypted before sending. If S/MIME support is built using OpenSSL, the value of the variable must be set to the name of a file that contains a certificate in PEM format. If S/MIME support is built using NSS, the value of this variable is ignored, but if multiple certificates for .I user@host are available, the .I smime-nickname-user@host variable should be set. Otherwise a certificate for the recipient is automatically retrieved from the certificate database, if possible. .IP If a message is sent to multiple recipients, each of them for whom a corresponding variable is set will receive an individually encrypted message; other recipients will continue to receive the message in plain text unless the .I smime-force-encryption variable is set. It is recommended to sign encrypted messages, i.\|e. to also set the .I smime-sign variable. .TP \fBsmime-nickname-\fIuser@host\fR Specifies the nickname of a certificate to be used when encrypting messages for .I user@host . Only applicable if S/MIME support is built using NSS. .TP .B smime-sign-cert Points to a file in PEM format that contains the user's private key as well as his certificate. Both are used with S/MIME for signing and decrypting messages. Only applicable if S/MIME support is built using OpenSSL. .TP \fBsmime-sign-cert-\fIuser@host\fR Overrides .I smime-sign-cert for the specific addresses. When signing messages and the value of the .I from variable is set to .IR user@host , the specific file is used. When decrypting messages, their recipient fields (To: and Cc:) are searched for addresses for which such a variable is set. .I Mailx always uses the first address that matches, so if the same message is sent to more than one of the user's addresses using different encryption keys, decryption might fail. Only applicable if S/MIME support is built using OpenSSL. .TP .B smime-sign-nickname Specifies that the named certificate be used for signing mail. If this variable is not set, but a single certificate matching the current .I from address is found in the database, that one is used automatically. Only applicable if S/MIME support is built using NSS. .TP \fBsmime-sign-nickname-\fIuser@host\fR Overrides .I smime-sign-nickname for a specific address. Only applicable if S/MIME support is built using NSS. .TP .B smtp Normally, \fImailx\fR invokes .IR sendmail (8) directly to transfer messages. If the \fIsmtp\fR variable is set, a SMTP connection to the server specified by the value of this variable is used instead. If the SMTP server does not use the standard port, a value of \fIserver:port\fR can be given, with \fIport\fR as a name or as a number. .IP There are two possible methods to get SSL/TLS encrypted SMTP sessions: First, the STARTTLS command can be used to encrypt a session after it has been initiated, but before any user-related data has been sent; see .I \%smtp-use-starttls above. Second, some servers accept sessions that are encrypted from their beginning on. This mode is configured by assigning \fBsmtps://\fIserver\fR[\fB:\fIport\fR] to the .I smtp variable. .IP The SMTP transfer is executed in a child process; unless either the .I sendwait or the .I verbose variable is set, this process runs asynchronously. If it receives a TERM signal, it will abort and save the message to the `dead.letter' file. .TP .B smtp-auth Sets the SMTP authentication method. If set to `login', or if unset and smtp-auth-user is set, AUTH LOGIN is used. If set to `cram-md5', AUTH CRAM-MD5 is used; if set to `plain', AUTH PLAIN is used. Otherwise, no SMTP authentication is performed. .TP \fBsmtp-auth-\fIuser\fB@\fIhost\fR Overrides .I smtp-auth for specific values of sender addresses, depending on the .I from variable. .TP .B smtp-auth-password Sets the global password for SMTP AUTH. Both user and password have to be given for AUTH LOGIN and AUTH CRAM-MD5. .TP \fBsmtp-auth-password-\fIuser\fB@\fIhost\fR Overrides .I smtp-auth-password for specific values of sender addresses, depending on the .I from variable. .TP .B smtp-auth-user Sets the global user name for SMTP AUTH. Both user and password have to be given for AUTH LOGIN and AUTH CRAM-MD5. .IP If this variable is set but neither .I smtp-auth-password or a matching .I smtp-auth-password-user@host can be found, .I mailx will as for a password on the user's terminal. .TP \fBsmtp-auth-user-\fIuser\fB@\fIhost\fR Overrides .I smtp-auth-user for specific values of sender addresses, depending on the .I from variable. .TP .B ssl-ca-dir Specifies a directory with CA certificates for verification of SSL/TLS server certificates. See .IR SSL_CTX_load_verify_locations (3) for more information. Only applicable if SSL/TLS support is built using OpenSSL. .TP .B ssl-ca-file Specifies a file with CA certificates for verification of SSL/TLS server certificates. See .IR SSL_CTX_load_verify_locations (3) for more information. Only applicable if SSL/TLS support is built using OpenSSL. .TP .B ssl-cert Sets the file name for a SSL/TLS client certificate required by some servers. Only applicable if SSL/TLS support is built using OpenSSL. .TP \fBssl-cert-\fIuser\fB@\fIhost\fR Sets an account-specific file name for a SSL/TLS client certificate required by some servers. Overrides .I ssl-cert for the specified account. Only applicable if SSL/TLS support is built using OpenSSL. .TP .B ssl-cipher-list Specifies a list of ciphers for SSL/TLS connections. See ciphers(1) for more information. Only applicable if SSL/TLS support is built using OpenSSL. .TP .B ssl-crl-file Specifies a file that contains a CRL in PEM format to use when verifying SSL/TLS server certificates. Only applicable if SSL/TLS support is built using OpenSSL. .TP .B ssl-crl-dir Specifies a directory that contains files with CRLs in PEM format to use when verifying SSL/TLS server certificates. Only applicable if SSL/TLS support is built using OpenSSL. .TP .B ssl-key Sets the file name for the private key of a SSL/TLS client certificate. If unset, the name of the certificate file is used. The file is expected to be in PEM format. Only applicable if SSL/TLS support is built using OpenSSL. .TP \fBssl-key-\fIuser\fB@\fIhost\fR Sets an account-specific file name for the private key of a SSL/TLS client certificate. Overrides .I ssl-key for the specified account. Only applicable if SSL/TLS support is built using OpenSSL. .TP .B ssl-method Selects a SSL/TLS protocol version; valid values are `ssl2', `ssl3', and `tls1'. If unset, the method is selected automatically, if possible. .TP \fBssl-method-\fIuser\fB@\fIhost\fR Overrides .I ssl-method for a specific account. .TP .B ssl-rand-egd Gives the pathname to an entropy daemon socket, see .IR RAND_egd (3). .TP .B ssl-rand-file Gives the pathname to a file with entropy data, see .IR RAND_load_file (3). If the file is a regular file writable by the invoking user, new data is written to it after it has been loaded. Only applicable if SSL/TLS support is built using OpenSSL. .TP .B ssl-verify Sets the action to be performed if an error occurs during SSL/TLS server certificate validation. Valid values are `strict' (fail and close connection immediately), `ask' (ask whether to continue on standard input), `warn' (print a warning and continue), `ignore' (do not perform validation). The default is `ask'. .TP \fBssl-verify-\fIuser\fB@\fIhost\fR Overrides .I ssl-verify for a specific account. .TP .B toplines If defined, gives the number of lines of a message to be printed out with the top command; normally, the first five lines are printed. .TP .B ttycharset The character set of the terminal \fImailx\fR operates on. There is normally no need to set this variable since \fImailx\fR can determine this automatically by looking at the LC_CTYPE locale setting; if this succeeds, the value is assigned at startup and will be displayed by the \fIset\fP command. Note that this is not necessarily a character set name that can be used in Internet messages. .TP .B VISUAL Pathname of the text editor to use in the visual command and ~v escape. .SH ENVIRONMENT VARIABLES Besides the variables described above, \fImailx\fR uses the following environment strings: .TP .B HOME The user's home directory. .TP \fBLANG\fR, \fBLC_ALL\fR, \fBLC_COLLATE\fR, \fBLC_CTYPE\fR, \fBLC_MESSAGES\fR See .IR locale (7). .TP .B MAILRC Is used as startup file instead of ~/.mailrc if set. When .I mailx scripts are invoked on behalf of other users, this variable should be set to `/dev/null' to avoid side-effects from reading their configuration files. .TP .B NAILRC If this variable is set and .I MAILRC is not set, it is read as startup file. .TP .B SYSV3 Changes the letters printed in the first column of a header summary. .TP .B TMPDIR Used as directory for temporary files instead of /tmp, if set. .SH FILES .TP ~/.mailrc File giving initial commands. .TP /etc/nail.rc System wide initialization file. .TP ~/.mime.types Personal MIME types. .TP /etc/mime.types System wide MIME types. .SH EXAMPLES .SS "Getting started" The .I mailx command has two distinct usages, according to whether one wants to send or receive mail. Sending mail is simple: to send a message to a user whose email address is, say, , use the shell command: .nf .sp $ \fBmailx\fI bill@host.example\fR .sp .fi then type your message. .I Mailx will prompt you for a message .I subject first; after that, lines typed by you form the body of the message. When you reach the end of the message, type an EOT (control\-d) at the beginning of a line, which will cause .I mailx to echo `EOT' and return you to the shell. .PP If, while you are composing the message you decide that you do not wish to send it after all, you can abort the letter with a \s-2RUBOUT\s0. Typing a single \s-2RUBOUT\s0 causes .I mailx to print `(Interrupt -- one more to kill letter)'. Typing a second \s-2RUBOUT\s0 causes .I mailx to save your partial letter on the file `dead.letter' in your home directory and abort the letter. Once you have sent mail to someone, there is no way to undo the act, so be careful. .PP If you want to send the same message to several other people, you can list their email addresses on the command line. Thus, .nf .sp $ \fBmailx\fI sam@workstation.example bob@server.example\fR Subject: Fees Tuition fees are due next Friday. Don't forget! EOT $ .sp .fi will send the reminder to \fI\fR. and \fI\fR. .PP To read your mail, simply type .nf .sp $ \fBmailx\fR .sp .fi .I Mailx will respond by typing its version number and date and then listing the messages you have waiting. Then it will type a prompt and await your command. The messages are assigned numbers starting with 1\(emyou refer to the messages with these numbers. .I Mailx keeps track of which messages are .I new (have been sent since you last read your mail) and .I read (have been read by you). New messages have an .B N next to them in the header listing and old, but unread messages have a .B U next to them. .I Mailx keeps track of new/old and read/unread messages by putting a header field called .I Status into your messages. .PP To look at a specific message, use the .I type command, which may be abbreviated to simply .I t . For example, if you had the following messages: .nf .sp O 1 drfoo@myhost.example Wed Sep 1 19:52 18/631 "Fees" O 2 sam@friends.example Thu Sep 2 00:08 30/895 .sp .fi you could examine the first message by giving the command: .nf .sp \fBtype\fR 1 .sp .fi which might cause .N mailx to respond with, for example: .nf .sp Message 1: From drfoo@myhost.example Wed Sep 1 19:52:25 2004 Subject: Fees Status: R Tuition fees are due next Wednesday. Don't forget! .sp .fi .PP Many .I mailx commands that operate on messages take a message number as an argument like the .I type command. For these commands, there is a notion of a current message. When you enter the .I mailx program, the current message is initially the first (or the first recent) one. Thus, you can often omit the message number and use, for example, .nf .sp \fBt\fR .sp .fi to type the current message. As a further shorthand, you can type a message by simply giving its message number. Hence, .nf .sp 1 .sp .fi would type the first message. .PP Frequently, it is useful to read the messages in your mailbox in order, one after another. You can read the next message in .I mailx by simply typing a newline. As a special case, you can type a newline as your first command to .I mailx to type the first message. .PP If, after typing a message, you wish to immediately send a reply, you can do so with the .I reply command. This command, like .IR type , takes a message number as an argument. .I mailx then begins a message addressed to the user who sent you the message. You may then type in your letter in reply, followed by a at the beginning of a line, as before. .PP Note that .I mailx copies the subject header from the original message. This is useful in that correspondence about a particular matter will tend to retain the same subject heading, making it easy to recognize. If there are other header fields in the message, like `Cc:', the information found will also be used. .PP Sometimes you will receive a message that has been sent to several people and wish to reply only to the person who sent it. .I Reply with a capital .I R replies to a message, but sends a copy to the sender only. .PP If you wish, while reading your mail, to send a message to someone, but not as a reply to one of your messages, you can send the message directly with the .I mail command, which takes as arguments the names of the recipients you wish to send to. For example, to send a message to , you would do: .nf .sp \fBmail\fI frank@machine.example\fR .fi .PP To delete a message from the mail folder, you can use the .I delete command. In addition to not saving deleted messages, .I mailx will not let you type them, either. The effect is to make the message disappear altogether, along with its number. .PP Many features of .I mailx can be tailored to your liking with the .I set command. The .I set command has two forms, depending on whether you are setting a .I binary option or a .I valued option. Binary options are either on or off. For example, the .I askcc option informs .I mailx that each time you send a message, you want it to prompt you for a `Cc:' header, to be included in the message. To set the .I askcc option, you would type .nf .sp \fBset\fR askcc .fi .PP Valued options are values which .I mailx uses to adapt to your tastes. For example, the .I record option tells .I mailx where to save messages sent by you, and is specified by .nf .sp \fBset\fR record=Sent .sp .fi for example. Note that no spaces are allowed in .I "set record=Sent". .PP .I Mailx includes a simple facility for maintaining groups of messages together in folders. To use the folder facility, you must tell .I mailx where you wish to keep your folders. Each folder of messages will be a single file. For convenience, all of your folders are kept in a single directory of your choosing. To tell .I mailx where your folder directory is, put a line of the form .nf .sp \fBset folder=\fIletters\fR .sp .fi in your .I .mailrc file. If, as in the example above, your folder directory does not begin with a `/', .I mailx will assume that your folder directory is to be found starting from your home directory. .PP Anywhere a file name is expected, you can use a folder name, preceded with `+'. For example, to put a message into a folder with the .I save command, you can use: .nf .sp \fBsave +\fIclasswork\fR .sp .fi to save the current message in the .I classwork folder. If the .I classwork folder does not yet exist, it will be created. Note that messages which are saved with the .I save command are automatically removed from your system mailbox. .PP In order to make a copy of a message in a folder without causing that message to be removed from your system mailbox, use the .I copy command, which is identical in all other respects to the .I save command. .PP The .I folder command can be used to direct .I mailx to the contents of a different folder. For example, .nf .sp \fBfolder +\fIclasswork\fR .sp .fi directs .I mailx to read the contents of the .I classwork folder. All of the commands that you can use on your system mailbox are also applicable to folders, including .IR type , .IR delete , and .IR reply . To inquire which folder you are currently editing, use simply: .nf .sp \fBfolder\fR .fi .PP To list your current set of folders, use the .I folders command. .PP Finally, the .I help command is available to print out a brief summary of the most important .I mailx commands. .PP While typing in a message to be sent to others, it is often useful to be able to invoke the text editor on the partial message, print the message, execute a shell command, or do some other auxiliary function. .I Mailx provides these capabilities through .I "tilde escapes" , which consist of a tilde (~) at the beginning of a line, followed by a single character which indicates the function to be performed. For example, to print the text of the message so far, use: .nf .sp \fB~p\fR .sp .fi which will print a line of dashes, the recipients of your message, and the text of the message so far. A list of the most important tilde escapes is available with `~?'. .SS "IMAP or POP3 client setup" First you need the following data from your ISP: the host name of the IMAP or POP3 server, user name and password for this server, and a notice whether the server uses SSL/TLS encryption. Assuming the host name is `server.myisp.example' and your user name for that server is `mylogin', you can refer to this account using the .I folder command or .I \-f command line option with .nf \fBimaps://\fImylogin\fB@\fIserver.myisp.example\fR .fi (This string is not necessarily the same as your Internet mail address.) You can replace `imaps://' with `imap://' if the server does not support SSL/TLS. (If SSL/TLS support is built using NSS, the .I nss-config-dir variable must be set before a connection can be initiated, see above). Use `pop3s://' or `pop3://' if the server does not offer IMAP. You should use IMAP if you can, though; first because it requires fewer network operations than POP3 to get the contents of the mailbox and is thus faster; and second because message attributes are maintained by the IMAP server, so you can easily distinguish new and old messages each time you connect. Even if the server does not accept IMAPS or POP3S connections, it is possible that it supports the STARTTLS method to make a session SSL/TLS encrypted after the initial connection has been performed, but before authentication begins. The only reliable method to see if this works is to try it; enter one of .nf \fBset imap-use-starttls\fR \fBset pop3-use-starttls\fR .fi before you initiate the connection. .PP As you probably want messages to be deleted from this account after saving them, prefix it with `\fI%:\fR'. The .I shortcut command can be used to avoid typing that many characters every time you want to connect: .nf \fBshortcut \fImyisp\fB \fB%:imaps://\fImylogin\fB@\fIserver.myisp.example\fR .fi You might want to put this string into a startup file. As the .I shortcut command is specific to this implementation of .I mailx and will confuse other implementations, it should not be used in .IR ~/.mailrc , instead, put .nf \fBset NAIL_EXTRA_RC=\fI~/.nailrc\fR .fi in .I ~/.mailrc and create a file .I ~/.nailrc containing the .I shortcut command above. You can then access your remote mailbox by invoking `mailx \-f \fImyisp\fR' on the command line, or by executing `fi \fImyisp\fR' within mailx. .PP If you want to use more than one IMAP mailbox on a server, or if you want to use the IMAP server for mail storage too, the .I account command (which is also \fImailx-\fRspecific) is more appropriate than the .I shortcut command. You can put the following in .IR ~/.nailrc : .sp .nf \fBaccount \fImyisp \fB{\fR \fBset folder=imaps://\fImylogin\fB@\fIserver.myisp.example\fR \fBset record=+\fISent \fBMBOX=+\fImbox \fBoutfolder\fR \fB}\fR .fi .sp and can then access incoming mail for this account by invoking `mailx \-A \fImyisp\fR' on the command line, or by executing `ac \fImyisp\fR' within mailx. After that, a command like `copy \fI1\fR +\fIotherfolder\fR' will refer to \fIotherfolder\fR on the IMAP server. In particular, `fi &' will change to the .I mbox folder, and `fi +Sent' will show your recorded sent mail, with both folders located on the IMAP server. .PP .I Mailx will ask you for a password string each time you connect to a remote account. If you can reasonably trust the security of your workstation, you can give this password in the startup file as .nf \fBset password-\fImylogin\fB@\fIserver.myisp.example\fB="\fISECRET\fB"\fR .fi You should change the permissions of this file to 0600, see .IR chmod (1). .PP .I Mailx supports different authentication methods for both IMAP and POP3. If Kerberos is used at your location, you can try to activate GSSAPI-based authentication by .nf \fBset imap-auth=gssapi\fR .fi The advantage of this method is that .I mailx does not need to know your password at all, nor needs to send sensitive data over the network. Otherwise, the options .nf \fBset imap-auth=cram-md5\fR \fBset pop3-use-apop\fR .fi for IMAP and POP3, respectively, offer authentication methods that avoid to send the password in clear text over the network, which is especially important if SSL/TLS cannot be used. If the server does not offer any of these authentication methods, conventional user/password based authentication must be used. It is sometimes helpful to set the .I verbose option when authentication problems occur. .I Mailx will display all data sent to the server in clear text on the screen with this option, including passwords. You should thus take care that no unauthorized person can look at your terminal when this option is set. .PP If you regularly use the same workstation to access IMAP accounts, you can greatly enhance performance by enabling local caching of IMAP messages. For any message that has been fully or partially fetched from the server, a local copy is made and is used when the message is accessed again, so most data is transferred over the network once only. To enable the IMAP cache, select a local directory name and put .nf \fBset imap-cache=\fI~/localdirectory\fR .fi in the startup file. All files within that directory can be overwritten or deleted by \fImailx\fR at any time, so you should not use the directory to store other information. .PP Once the cache contains some messages, it is not strictly necessary anymore to open a connection to the IMAP server to access them. When \fImailx\fR is invoked with the \fI\-D\fR option, or when the .I disconnected variable is set, only cached data is used for any folder you open. Messages that have not yet been completely cached are not available then, but all other messages can be handled as usual. Changes made to IMAP mailboxes in .I disconnected mode are committed to the IMAP server next time it is used in .I online mode. Synchronizing the local status with the status on the server is thus partially within your responsibility; if you forget to initiate a connection to the server again before you leave your location, changes made on one workstation are not available on others. Also if you alter IMAP mailboxes from a workstation while uncommitted changes are still pending on another, the latter data may become invalid. The same might also happen because of internal server status changes. You should thus carefully evaluate this feature in your environment before you rely on it. .PP Many servers will close the connection after a short period of inactivity. Use one of .nf \fBset pop3-keepalive=\fI30\fR \fBset imap-keepalive=\fI240\fR .fi to send a keepalive message each 30 seconds for POP3, or each 4 minutes for IMAP. .PP If you encounter problems connecting to a SSL/TLS server, try the .I ssl-rand-egd and .I ssl-rand-file variables (see the OpenSSL FAQ for more information) or specify the protocol version with .IR ssl-method . Contact your ISP if you need a client certificate or if verification of the server certificate fails. If the failed certificate is indeed valid, fetch its CA certificate by executing the shell command .nf $ \fBopenssl s_client &1 | tee \fIlog\fR .fi (see .IR s_client (1)) and put it into the file specified with .IR ssl-ca-file . The data you need is located at the end of the certificate chain within (and including) the `BEGIN CERTIFICATE' and `END CERTIFICATE' lines. (Note that it is possible to fetch a forged certificate by this method. You can only completely rely on the authenticity of the CA certificate if you fetch it in a way that is trusted by other means, such as by personally receiving the certificate on storage media.) .SS "Creating a score file or message filter" The scoring commands are best separated from other configuration for clarity, and are mostly .I mailx specific. It is thus recommended to put them in a separate file that is sourced from your NAIL_EXTRA_RC as follows: .nf .sp \fBsource\fI ~/.scores\fR .sp .fi The \fI.scores\fR file could then look as follows: .nf .sp \fBdefine\fR \fIlist\fR { \fBscore\fR (subject "important discussion") +10 \fBscore\fR (subject "annoying discussion") \-10 \fBscore\fR (from "nicefellow@goodnet") +15 \fBscore\fR (from "badguy@poornet") \-5 \fBmove\fR (header x-spam-flag "+++++") \fI+junk\fR } \fBset folder-hook-\fRimap://user@host/public.list=\fIlist\fR .sp .fi In this scheme, you would see any mail from `nicefellow@goodnet', even if the surrounding discussion is annoying; but you normally would not see mail from `badguy@poornet', unless he participates in the important discussion. Messages that are marked with five or more plus characters in their `X-Spam-Flag' field (inserted by some server-side filtering software) are moved to the folder `junk' in the .I folder directory. .PP Be aware that all criteria in (\|) lead to substring matches, so you would also score messages from e.\|g. `notsobadguy@poornetmakers' negative here. It is possible to select addresses exactly using \fI"address"\fR message specifications, but these cannot be executed remotely and will thus cause all headers to be downloaded from IMAP servers while looking for matches. .PP When searching messages on an IMAP server, best performance is usually achieved by sending as many criteria as possible in one large (\|) specification, because each single such specification will result in a separate network operation. .SS "Activating the Bayesian filter" The Bayesian junk mail filter works by examining the words contained in messages. You decide yourself what a good and what a bad message is. Thus the resulting filter is your very personal one; once it is correctly set up, it will filter only messages similar to those previously specified by you. .PP To use the Bayesian filter, a location for the junk mail database must be defined first: .nf .sp \fBset junkdb=\fI~/.junkdb\fR .sp .fi The junk mail database does not contain actual words extracted from messages, but hashed representations of them. A foreign person who can read the database could only examine the frequency of previously known words in your mail. .PP If you have sufficient disk space (several 10\ MB) available, it is recommended that you set the .I chained-junk-tokens option. The filter will then also consider two-word tokens, improving its accuracy. .PP A set of good messages and junk messages must now be available; it is also possible to use the incoming new messages for this purpose, although it will of course take some time until the filter becomes useful then. Do not underestimate the amount of statistical data needed; some hundred messages are typically necessary to get satisfactory results, and many thousand messages for best operation. You have to pass the good messages to the .I good command, and the junk messages to the .I junk command. If you ever accidentally mark a good message as junk or vice-versa, call the .I ungood or .I unjunk command to correct this. .PP Once a reasonable amount of statistics has been collected, new messages can be classified automatically. The .I classify command marks all messages that the filter considers to be junk, but it does not perform any action on them by default. It is recommended that you move these messages into a separate folder just for the case that false positives occur, or to pass them to the .I junk command later again to further improve the junk mail database. To automatically move incoming junk messages every time the inbox is opened, put lines like the following into your .I .scores file (or whatever name you gave to the file in the last example): .nf .sp \fBdefine\fR \fIjunkfilter\fR { \fBclassify (smaller \fI20000\fB) :n\fR \fBmove :j\fR \fI+junk\fR } \fBset folder-hook-\fRimap://user@host/INBOX=\fIjunkfilter\fR .sp .fi If you set the .I verbose option before running the .I classify command, .I mailx prints the words it uses for calculating the junk status along with their statistical probabilities. This can help you to find out why some messages are not classified as you would like them to be. To see the statistical probability of a given word, use the .I probability command. .PP If a junk message was not recognized as such, use the .I junk command to correct this. Also if you encounter a false positive (a good message that was wrongly classified as junk), pass it to the .I good command. .PP Since the .I classify command must examine the entire text of all new messages in the respective folder, this will also cause all of them to be downloaded from the IMAP server. You should thus restrict the size of messages for automatic filtering. If server-based filtering is also available, you might try if that works for you first. .SS "Reading HTML mail" You need either the .I w3m or .I lynx utility or another command-line web browser that can write plain text to standard output. .nf .sp \fBset pipe-text/html=\fR"w3m -dump -T text/html" .sp .fi or .nf .sp \fBset pipe-text/html=\fR"lynx -dump -force_html /dev/stdin" .sp .fi will then cause HTML message parts to be converted into a more friendly form. .SS "Viewing PDF attachments" Most PDF viewers do not accept input directly from a pipe. It is thus necessary to store the attachment in a temporary file, as with .nf .sp \fBset pipe-application/pdf=\fR"cat >/tmp/mailx$$.pdf; \e acroread /tmp/mailx$$.pdf; rm /tmp/mailx$$.pdf" .sp .fi Note that security defects are discovered in PDF viewers from time to time. Automatical command execution like this can compromise your system security, in particular if you stay not always informed about such issues. .SS "Signed and encrypted messages with S/MIME" S/MIME provides two central mechanisms: message signing and message encryption. A signed message contains some data in addition to the regular text. The data can be used to verify that the message was sent using a valid certificate, that the sender's address in the message header matches that in the certificate, and that the message text has not been altered. Signing a message does not change its regular text; it can be read regardless of whether the recipient's software is able to handle S/MIME. It is thus usually possible to sign all outgoing messages if so desired.\(emEncryption, in contrast, makes the message text invisible for all people except those who have access to the secret decryption key. To encrypt a message, the specific recipient's public encryption key must be known. It is thus not possible to send encrypted mail to people unless their key has been retrieved from either previous communication or public key directories. A message should always be signed before it is encrypted. Otherwise, it is still possible that the encrypted message text is altered. .PP A central concept to S/MIME is that of the certification authority (CA). A CA is a trusted institution that issues certificates. For each of these certificates, it can be verified that it really originates from the CA, provided that the CA's own certificate is previously known. A set of CA certificates is usually delivered with OpenSSL and installed on your system. If you trust the source of your OpenSSL software installation, this offers reasonable security for S/MIME on the Internet. In general, a certificate cannot be more secure than the method its CA certificate has been retrieved with, though. Thus if you download a CA certificate from the Internet, you can only trust the messages you verify using that certificate as much as you trust the download process. .PP The first thing you need for participating in S/MIME message exchange is your personal certificate, including a private key. The certificate contains public information, in particular your name and your email address, and the public key that is used by others to encrypt messages for you, and to verify signed messages they supposedly received from you. The certificate is included in each signed message you send. The private key must be kept secret. It is used to decrypt messages that were previously encrypted with your public key, and to sign messages. .PP For personal use, it is recommended that you get a S/MIME certificate from one of the major CAs on the Internet using your WWW browser. (Many CAs offer such certificates for free.) You will usually receive a combined certificate and private key in PKCS#12 format which .I mailx does not directly accept if S/MIME support is built using OpenSSL. To convert it to PEM format, use the following shell command: .nf .sp $ \fBopenssl pkcs12 \-in \fIcert.p12\fB \-out \fIcert.pem\fB \-clcerts \e \-nodes\fR .sp .fi If you omit the .I \-nodes parameter, you can specifiy an additional .I "PEM pass phrase" for protecting the private key. .I Mailx will then ask you for that pass phrase each time it signs or decrypts a message. You can then use .nf .sp \fBset smime-sign-cert-\fImyname@myisp.example\fB=\fIcert.pem\fR .sp .fi to make this private key and certificate known to .IR mailx . .PP If S/MIME support is built using NSS, the PKCS#12 file must be installed using Mozilla (provided that .I nss-config-dir is set appropriately, see above), and no further action is necessary unless multiple user certificates for the same email address are installed. In this case, the .I smime-sign-nickname variable has to be set appropriately. .PP You can now sign outgoing messages. Just use .nf .sp \fBset smime-sign\fR .sp .fi to do so. .PP From each signed message you send, the recipient can fetch your certificate and use it to send encrypted mail back to you. Accordingly if somebody sends you a signed message, you can do the same. First use the .I verify command to check the validity of the certificate. After that, retrieve the certificate and tell .I mailx that it should use it for encryption: .nf .sp \fBcertsave\fI filename\fR \fBset smime-encrypt-\fIuser@host\fB=\fIfilename\fR .sp .fi If S/MIME support is built using NSS, the saved certificate must be installed using Mozilla. The value of the .I smime-encrypt-user@host is ignored then, but if multiple certificates for the recipient are available, the .I smime-nickname-user@host variable must be set. .PP You should carefully consider if you prefer to store encrypted messages in decrypted form. If you do, anybody who has access to your mail folders can read them, but if you do not, you might be unable to read them yourself later if you happen to lose your private key. The .I decrypt command saves messages in decrypted form, while the .IR save , .IR copy , and .I move commands leave them encrypted. .PP Note that neither S/MIME signing nor encryption applies to message subjects or other header fields. Thus they may not contain sensitive information for encrypted messages, and cannot be trusted even if the message content has been verified. When sending signed messages, it is recommended to repeat any important header information in the message text. .SS "Using CRLs with S/MIME or SSL/TLS" Certification authorities (CAs) issue certificate revocation lists (CRLs) on a regular basis. These lists contain the serial numbers of certificates that have been declared invalid after they have been issued. Such usually happens because the private key for the certificate has been compromised, because the owner of the certificate has left the organization that is mentioned in the certificate, etc. To seriously use S/MIME or SSL/TLS verification, an up-to-date CRL is required for each trusted CA. There is otherwise no method to distinguish between valid and invalidated certificates. .I Mailx currently offers no mechanism to fetch CRLs, or to access them on the Internet, so you have to retrieve them by some external mechanism. .PP If S/MIME and SSL/TLS support are built using OpenSSL, .I mailx accepts CRLs in PEM format only; CRLs in DER format must be converted, e.\|g. with the shell command .nf .sp $ \fBopenssl crl \-inform DER \-in \fIcrl.der\fB \-out \fIcrl.pem\fR .sp .fi To tell .I mailx about the CRLs, a directory that contains all CRL files (and no other files) must be created. The .I smime-crl-dir or .I ssl-crl-dir variables, respectively, must then be set to point to that directory. After that, .I mailx requires a CRL to be present for each CA that is used to verify a certificate. .PP If S/MIME and SSL/TLS support are built using NSS, CRLs can be imported in Mozilla applications (provided that .I nss-config-dir is set appropriately). .SS "Sending mail from scripts" If you want to send mail from scripts, you must be aware that .I mailx reads the user's configuration files by default. So unless your script is only intended for your own personal use (as e.g. a cron job), you need to circumvent this by invoking .I mailx like .nf .sp \fBMAILRC=/dev/null mailx \-n\fR .sp .fi You then need to create a configuration for .I mailx for your script. This can be done by either pointing the .I MAILRC variable to a custom configuration file, or by passing the configuration in environment variables. Since many of the configuration options are not valid shell variables, the .I env command is useful in this situation. An invocation could thus look like .nf .sp \fBenv MAILRC=/dev/null\fR from=\fIscriptreply@domain\fR smtp=\fIhost\fR \e smtp-auth-user=\fIlogin\fR smtp-auth-password=\fIsecret\fR \e smtp-auth=\fIlogin\fR \fBmailx \-n\fR \-s "\fIsubject\fR" \e \-a \fIattachment_file\fR \fIrecipient@domain\fR <\fIcontent_file\fR .SH "SEE ALSO" fmt(1), newaliases(1), openssl(1), pg(1), more(1), vacation(1), ssl(3), aliases(5), locale(7), mailaddr(7), sendmail(8) .SH NOTES .PP Variables in the environment passed to .I mailx cannot be unset. .PP The character set conversion relies on the .IR iconv (3) function. Its functionality differs widely between the various system environments \fImailx\fR runs on. If the message `Cannot convert from \fIa\fR to \fIb\fR' appears, either some characters within the message header or text are not appropriate for the currently selected terminal character set, or the needed conversion is not supported by the system. In the first case, it is necessary to set an appropriate LC_CTYPE locale (e.\|g. \fIen_US\fR) or the .I ttycharset variable. In the second case, the .I sendcharsets and .I ttycharset variables must be set to the same value to inhibit character set conversion. If .I iconv() is not available at all, the value assigned to .I sendcharsets must match the character set that is used on the terminal. .PP Mailx expects input text to be in Unix format, with lines separated by .I newline (^J, \en) characters only. Non-Unix text files that use .I carriage return (^M, \er) characters in addition will be treated as binary data; to send such files as text, strip these characters e.\ g. by .RS .sp tr \-d '\e015' , and ``Better Bayesian Filtering'', January 2003, \%. Chained tokens are due to a paper by Jonathan A. Zdziarski, ``Advanced Language Classification using Chained Tokens'', February 2004, \%. .PP A \fImail\fR command appeared in Version 1 AT&T Unix. Berkeley Mail was written in 1978 by Kurt Shoens. This man page is derived from from The Mail Reference Manual originally written by Kurt Shoens. \fIHeirloom Mailx\fR enhancements are maintained and documented by Gunnar Ritter. .PP Portions of this text are reprinted and reproduced in electronic form from IEEE Std 1003.1, 2003 Edition, Standard for Information Technology \(em Operating System Interface (POSIX), The Open Group Base Specifications Issue 6, Copyright \(co 2001-2003 by the Institute of Electrical and Electronics Engineers, Inc and The Open Group. In the event of any discrepancy between this version and the original IEEE and The Open Group Standard, the original IEEE and The Open Group Standard is the referee document. The original Standard can be obtained online at \%http://www.opengroup.org/unix/online.html\ . Redistribution of this material is permitted so long as this notice remains intact. heirloom-mailx-12.5/mailx.spec000066400000000000000000000047761155563371200164030ustar00rootroot00000000000000# Sccsid @(#)mailx.spec 1.35 (gritter) 12/25/06 %define use_nss 0 %define mozilla_version 1.0.5 Summary: An enhanced implementation of the mailx command Name: mailx Version: 12.5 Release: 1 License: BSD Group: Applications/Internet Source: %{name}-%{version}.tar.bz2 URL: Vendor: Gunnar Ritter Packager: Didar Hussain BuildRoot: %{_tmppath}/%{name}-root %if %{use_nss} Requires: seamonkey-nss Requires: seamonkey-nspr BuildRequires: seamonkey-nss-devel BuildRequires: seamonkey-nspr-devel BuildRequires: /usr/include/mozilla-seamonkey-%{mozilla_version}/nss/cms.h %endif %description Heirloom mailx is derived from Berkeley Mail and is intended provide the functionality of the POSIX mailx command with additional support for MIME messages, IMAP, POP3, and SMTP. It provides enhanced features for interactive use, such as caching and disconnected operation for IMAP, message threading, scoring, and filtering. It is also usable as a mail batch language, both for sending and receiving mail. %define prefix /usr %define bindir %{prefix}/bin %define mandir %{prefix}/share/man %define sysconfdir /etc %define mailrc %{sysconfdir}/nail.rc %define mailspool /var/mail %define sendmail /usr/lib/sendmail %define ucbinstall install %define cflags -O2 -fomit-frame-pointer %define cppflags -D_GNU_SOURCE %define makeflags PREFIX=%{prefix} BINDIR=%{bindir} MANDIR=%{mandir} SYSCONFDIR=%{sysconfdir} MAILRC=%{mailrc} MAILSPOOL=%{mailspool} SENDMAIL=%{sendmail} UCBINSTALL=%{ucbinstall} CFLAGS="%{cflags}" CPPFLAGS="%{cppflags}" %prep %setup %build rm -rf %{buildroot} # Some RedHat releases refuse to compile with OpenSSL unless # -I/usr/kerberos/include is given. To compile with GSSAPI authentication # included, they also need -L/usr/kerberos/lib. test -d /usr/kerberos/include && INCLUDES="$INCLUDES -I/usr/kerberos/include" export INCLUDES test -d /usr/kerberos/lib && LDFLAGS="$LDFLAGS -L/usr/kerberos/lib" export LDFLAGS %if %{use_nss} INCLUDES="$INCLUDES -I/usr/include/mozilla-seamonkey-%{mozilla_version}/nspr" INCLUDES="$INCLUDES -I/usr/include/mozilla-seamonkey-%{mozilla_version}/nss" export INCLUDES %endif make %{makeflags} %install make DESTDIR=%{buildroot} %{makeflags} install gzip -9 %{buildroot}/%{mandir}/man1/mailx.1 %clean cd ..; rm -rf %{_builddir}/%{name}-%{version} rm -rf %{buildroot} %files %defattr(-,root,root) %doc COPYING AUTHORS INSTALL README TODO ChangeLog %config(noreplace) /etc/nail.rc %{bindir}/mailx %{mandir}/man1/mailx* heirloom-mailx-12.5/main.c000066400000000000000000000325541155563371200155000ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char copyright[] = "@(#) Copyright (c) 1980, 1993 The Regents of the University of California. All rights reserved.\n"; static char sccsid[] = "@(#)main.c 2.51 (gritter) 10/1/07"; #endif /* DOSCCS */ #endif /* not lint */ /* * Most strcpy/sprintf functions have been changed to strncpy/snprintf to * correct several buffer overruns (at least one ot them was exploitable). * Sat Jun 20 04:58:09 CEST 1998 Alvaro Martinez Echevarria * --- * Note: We set egid to realgid ... and only if we need the egid we will * switch back temporary. Nevertheless, I do not like seg faults. * Werner Fink, */ #include "config.h" #ifdef HAVE_NL_LANGINFO #include #endif /* HAVE_NL_LANGINFO */ #define _MAIL_GLOBS_ #include "rcv.h" #include "extern.h" #include #include #include #include #include #ifdef HAVE_SETLOCALE #include #endif /* HAVE_SETLOCALE */ /* * Mail -- a mail program * * Startup -- interface with user. */ static sigjmp_buf hdrjmp; char *progname; sighandler_type dflpipe = SIG_DFL; static void hdrstop(int signo); static void setscreensize(int dummy); int main(int argc, char *argv[]) { const char optstr[] = "A:BHEFINVT:RS:a:b:c:dDefh:inqr:s:tu:v~"; int i, existonly = 0, headersonly = 0, sendflag = 0; struct name *to, *cc, *bcc, *smopts; struct attachment *attach; char *subject, *cp, *ef, *qf = NULL, *fromaddr = NULL, *Aflag = NULL; char nosrc = 0; int Eflag = 0, Fflag = 0, Nflag = 0, tflag = 0; sighandler_type prevint; (void)&Nflag; /* * Absolutely the first thing we do is save our egid * and set it to the rgid, so that we can safely run * setgid. We use the sgid (saved set-gid) to allow ourselves * to revert to the egid if we want (temporarily) to become * priveliged. */ effectivegid = getegid(); realgid = getgid(); if (setgid(realgid) < 0) { perror("setgid"); exit(1); } starting = 1; progname = strrchr(argv[0], '/'); if (progname != NULL) progname++; else progname = argv[0]; /* * Set up a reasonable environment. * Figure out whether we are being run interactively, * start the SIGCHLD catcher, and so forth. */ safe_signal(SIGCHLD, sigchild); is_a_tty[0] = isatty(0); is_a_tty[1] = isatty(1); if (is_a_tty[0]) { assign("interactive", ""); if (is_a_tty[1]) safe_signal(SIGPIPE, dflpipe = SIG_IGN); } assign("header", ""); assign("save", ""); #ifdef HAVE_SETLOCALE setlocale(LC_CTYPE, ""); setlocale(LC_COLLATE, ""); setlocale(LC_MESSAGES, ""); mb_cur_max = MB_CUR_MAX; #if defined (HAVE_NL_LANGINFO) && defined (CODESET) if (value("ttycharset") == NULL && (cp = nl_langinfo(CODESET)) != NULL) assign("ttycharset", cp); #endif /* HAVE_NL_LANGINFO && CODESET */ #if defined (HAVE_MBTOWC) && defined (HAVE_WCTYPE_H) if (mb_cur_max > 1) { wchar_t wc; if (mbtowc(&wc, "\303\266", 2) == 2 && wc == 0xF6 && mbtowc(&wc, "\342\202\254", 3) == 3 && wc == 0x20AC) utf8 = 1; } #endif /* HAVE_MBTOWC && HAVE_WCTYPE_H */ #else /* !HAVE_SETLOCALE */ mb_cur_max = 1; #endif /* !HAVE_SETLOCALE */ #ifdef HAVE_CATGETS #ifdef NL_CAT_LOCALE i = NL_CAT_LOCALE; #else i = 0; #endif catd = catopen(CATNAME, i); #endif /* HAVE_CATGETS */ #ifdef HAVE_ICONV iconvd = (iconv_t) -1; #endif image = -1; /* * Now, determine how we are being used. * We successively pick off - flags. * If there is anything left, it is the base of the list * of users to mail to. Argp will be set to point to the * first of these users. */ ef = NULL; to = NULL; cc = NULL; bcc = NULL; attach = NULL; smopts = NULL; subject = NULL; while ((i = getopt(argc, argv, optstr)) != EOF) { switch (i) { case 'V': puts(version); exit(0); /*NOTREACHED*/ case 'B': setvbuf(stdin, NULL, _IOLBF, 0); setvbuf(stdout, NULL, _IOLBF, 0); break; case 'H': headersonly = 1; break; case 'E': Eflag = 1; break; case 'F': Fflag = 1; sendflag++; break; case 'S': { char *args[] = { NULL, NULL }; args[0] = optarg; set(args); } break; case 'T': /* * Next argument is temp file to write which * articles have been read/deleted for netnews. */ Tflag = optarg; if ((i = creat(Tflag, 0600)) < 0) { perror(Tflag); exit(1); } close(i); /*FALLTHRU*/ case 'I': /* * Show Newsgroups: field in header summary */ Iflag = 1; break; case 'u': /* * Next argument is person to pretend to be. */ uflag = myname = optarg; break; case 'i': /* * User wants to ignore interrupts. * Set the variable "ignore" */ assign("ignore", ""); break; case 'd': debug++; break; case 'D': assign("disconnected", ""); break; case 'e': existonly++; break; case 's': /* * Give a subject field for sending from * non terminal */ subject = optarg; sendflag++; break; case 'f': /* * User is specifying file to "edit" with Mail, * as opposed to reading system mailbox. * If no argument is given, we read his * mbox file. * * Check for remaining arguments later. */ ef = "&"; break; case 'q': /* * User is specifying file to quote in front of * the mail to be collected. */ if ((argv[optind]) && (argv[optind][0] != '-')) qf = argv[optind++]; else qf = NULL; sendflag++; break; case 'n': /* * User doesn't want to source /usr/lib/Mail.rc */ nosrc++; break; case 'N': /* * Avoid initial header printing. */ Nflag = 1; unset_internal("header"); break; case 'v': /* * Send mailer verbose flag */ assign("verbose", ""); break; case 'r': /* * Set From address. */ fromaddr = optarg; smopts = cat(smopts, nalloc("-r", 0)); smopts = cat(smopts, nalloc(optarg, 0)); tildeflag = -1; sendflag++; break; case 'a': /* * Get attachment filenames */ if ((attach = add_attachment(attach, optarg)) == NULL) { perror(optarg); exit(1); } sendflag++; break; case 'c': /* * Get Carbon Copy Recipient list */ cc = checkaddrs(cat(cc, extract(optarg, GCC|GFULL))); sendflag++; break; case 'b': /* * Get Blind Carbon Copy Recipient list */ bcc = checkaddrs(cat(bcc, extract(optarg, GBCC|GFULL))); sendflag++; break; case 'h': /* * Hop count for sendmail */ smopts = cat(smopts, nalloc("-h", 0)); smopts = cat(smopts, nalloc(optarg, 0)); sendflag++; break; case '~': if (tildeflag == 0) tildeflag = 1; break; case 't': sendflag = 1; tflag = 1; break; case 'A': Aflag = optarg; break; case 'R': Rflag = 1; break; case '?': usage: fprintf(stderr, catgets(catd, CATSET, 135, "Usage: %s -eiIUdEFntBDNHRV~ -T FILE -u USER -h hops -r address -s SUBJECT -a FILE -q FILE -f FILE -A ACCOUNT -b USERS -c USERS -S OPTION users\n"), progname); exit(2); } } if (ef != NULL) { if (optind < argc) { if (optind + 1 < argc) { fprintf(stderr, catgets(catd, CATSET, 205, "More than one file given with -f\n")); goto usage; } ef = argv[optind]; } } else { for (i = optind; argv[i]; i++) to = checkaddrs(cat(to, extract(argv[i], GTO|GFULL))); } /* * Check for inconsistent arguments. */ if (ef != NULL && to != NULL) { fprintf(stderr, catgets(catd, CATSET, 137, "Cannot give -f and people to send to.\n")); goto usage; } if (sendflag && !tflag && to == NULL) { fprintf(stderr, catgets(catd, CATSET, 138, "Send options without primary recipient specified.\n")); goto usage; } if (Rflag && to != NULL) { fprintf(stderr, "The -R option is meaningless in send mode.\n"); goto usage; } if (Iflag && ef == NULL) { fprintf(stderr, catgets(catd, CATSET, 204, "Need -f with -I.\n")); goto usage; } tinit(); setscreensize(0); #ifdef SIGWINCH if (value("interactive")) if (safe_signal(SIGWINCH, SIG_IGN) != SIG_IGN) safe_signal(SIGWINCH, setscreensize); #endif /* SIGWINCH */ input = stdin; rcvmode = !to && !tflag; spreserve(); if (!nosrc) load(MAILRC); /* * Expand returns a savestr, but load only uses the file name * for fopen, so it's safe to do this. */ if ((cp = getenv("MAILRC")) != NULL) load(expand(cp)); else if ((cp = getenv("NAILRC")) != NULL) load(expand(cp)); else load(expand("~/.mailrc")); if (getenv("NAIL_EXTRA_RC") == NULL && (cp = value("NAIL_EXTRA_RC")) != NULL) load(expand(cp)); /* * Now we can set the account. */ if (Aflag) { char *a[2]; a[0] = Aflag; a[1] = NULL; account(a); } /* * Override 'skipemptybody' if '-E' flag was given. */ if (Eflag) assign("skipemptybody", ""); starting = 0; /* * From address from command line overrides rc files. */ if (fromaddr) assign("from", fromaddr); if (!rcvmode) { mail(to, cc, bcc, smopts, subject, attach, qf, Fflag, tflag, Eflag); /* * why wait? */ exit(senderr ? 1 : 0); } /* * Ok, we are reading mail. * Decide whether we are editing a mailbox or reading * the system mailbox, and open up the right stuff. */ if (ef == NULL) ef = "%"; else if (*ef == '@') { /* * This must be treated specially to make invocation like * -A imap -f @mailbox work. */ if ((cp = value("folder")) != NULL && which_protocol(cp) == PROTO_IMAP) strncpy(mailname, cp, PATHSIZE)[PATHSIZE-1] = '\0'; } i = setfile(ef, 0); if (i < 0) exit(1); /* error already reported */ if (existonly) exit(i); if (headersonly) { if (mb.mb_type == MB_IMAP) imap_getheaders(1, msgCount); for (i = 1; i <= msgCount; i++) printhead(i, stdout, 0); exit(exit_status); } callhook(mailname, 0); if (i > 0 && value("emptystart") == NULL) exit(1); if (sigsetjmp(hdrjmp, 1) == 0) { if ((prevint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN) safe_signal(SIGINT, hdrstop); if (Nflag == 0) { if (value("quiet") == NULL) printf(catgets(catd, CATSET, 140, "Heirloom %s version %s. " "Type ? for help.\n"), value("bsdcompat") ? "Mail" : "mailx", version); announce(1); fflush(stdout); } safe_signal(SIGINT, prevint); } commands(); if (mb.mb_type == MB_FILE || mb.mb_type == MB_MAILDIR) { safe_signal(SIGHUP, SIG_IGN); safe_signal(SIGINT, SIG_IGN); safe_signal(SIGQUIT, SIG_IGN); } strncpy(mboxname, expand("&"), sizeof mboxname)[sizeof mboxname-1]='\0'; quit(); return exit_status; } /* * Interrupt printing of the headers. */ /*ARGSUSED*/ static void hdrstop(int signo) { fflush(stdout); fprintf(stderr, catgets(catd, CATSET, 141, "\nInterrupt\n")); siglongjmp(hdrjmp, 1); } /* * Compute what the screen size for printing headers should be. * We use the following algorithm for the height: * If baud rate < 1200, use 9 * If baud rate = 1200, use 14 * If baud rate > 1200, use 24 or ws_row * Width is either 80 or ws_col; */ /*ARGSUSED*/ static void setscreensize(int dummy) { struct termios tbuf; #ifdef TIOCGWINSZ struct winsize ws; #endif speed_t ospeed; #ifdef TIOCGWINSZ if (ioctl(1, TIOCGWINSZ, &ws) < 0) ws.ws_col = ws.ws_row = 0; #endif if (tcgetattr(1, &tbuf) < 0) ospeed = B9600; else ospeed = cfgetospeed(&tbuf); if (ospeed < B1200) scrnheight = 9; else if (ospeed == B1200) scrnheight = 14; #ifdef TIOCGWINSZ else if (ws.ws_row != 0) scrnheight = ws.ws_row; #endif else scrnheight = 24; #ifdef TIOCGWINSZ if ((realscreenheight = ws.ws_row) == 0) realscreenheight = 24; #endif #ifdef TIOCGWINSZ if ((scrnwidth = ws.ws_col) == 0) #endif scrnwidth = 80; } heirloom-mailx-12.5/makeconfig000066400000000000000000000252261155563371200164340ustar00rootroot00000000000000#!/bin/sh # # Sccsid @(#)makeconfig 1.44 (gritter) 5/26/09 # tmp=___build$$ tmp2=___tmp$$ out=config.h log=config.log lib=LIBS make="${MAKE-make}" exec 5>&2 >$log 2>&1 rm -f $out $lib echo "\ /* * Auto-generated by $0. * Changes are lost when $0 is run again. */ " >$out echo " # Auto-generated by $0. # Changes are lost when $0 is run again. # # need one non-empty line # to make grep happy ">$lib trap "rm -f $out $lib; exit" 1 2 15 trap "rm -f $tmp.? $tmp $tmp2.? $tmp2" 0 msg() { fmt=$1 shift printf "*** $fmt\\n" "$@" printf "$fmt" "$@" >&5 } compile_check() { variable=$1 topic=$2 define=$3 echo '************************************************************' msg "checking $topic ... " echo "/* checked $topic */" >>$out rm -f $tmp.o echo '*** test program is' tee $tmp.c #echo '*** the preprocessor generates' #$make $tmp.x #cat $tmp.x echo '*** results are' if $make $tmp.o && test -f $tmp.o then msg "okay\\n" echo "$define" >>$out eval have_$variable=yes return 0 else echo "/* $define */" >>$out msg "no\\n" eval unset have_$variable return 1 fi } link_check() { variable=$1 topic=$2 define=$3 libs=$4 echo '************************************************************' msg "checking $topic ... " echo "/* checked $topic */" >>$out cp $lib $tmp2 rm -f $tmp $tmp.o echo '*** test program is' tee $tmp.c #echo '*** the preprocessor generates' #$make $tmp.x #cat $tmp.x echo '*** results are' if $make $tmp LIBS="$LIBS $libs" && test -f $tmp then msg "okay\\n" echo "$define" >>$out echo "$libs" >$lib cat $tmp2 >>$lib eval have_$variable=yes return 0 else msg "no\\n" echo "/* $define */" >>$out eval unset have_$variable return 1 fi } link_check hello 'if a hello world program can be built' <<\! || { \ echo 'This problem is most certainly not specific to this software.' >&5; \ echo "Read the file '$log' and check your compiler installation." >&5; \ rm $out; exit 1; \ } #include int main(int argc, char *argv[]) { puts("hello world"); return 0; } ! compile_check alloca_h 'for ' '#define HAVE_ALLOCA_H' <<\! #include ! link_check alloca 'for alloca()' '#define HAVE_ALLOCA' <<\! #include "rcv.h" int main(void) { alloca(1); return 0; } ! compile_check ssize_t 'for ssize_t' '#define HAVE_SSIZE_T' <<\! #include #include int main(void) { ssize_t i = 3; write(1, "foo", i); return 0; } ! link_check snprintf 'for snprintf()' '#define HAVE_SNPRINTF' <<\! #include int main(void) { char b[20]; snprintf(b, sizeof b, "%s", "string"); return 0; } ! link_check fchdir 'for fchdir()' '#define HAVE_FCHDIR' <<\! #include int main(void) { fchdir(0); return 0; } ! link_check mmap 'for mmap()' '#define HAVE_MMAP' <<\! #include #include int main(void) { mmap(0, 0, 0, 0, 0, 0); return 0; } ! link_check mremap 'for mremap()' '#define HAVE_MREMAP' <<\! #include #include int main(void) { mremap(0, 0, 0, MREMAP_MAYMOVE); return 0; } ! cat >$tmp2.c <<\! #include int main(void) { iconv_t id; id = iconv_open("foo", "bar"); return 0; } ! <$tmp2.c link_check iconv 'for iconv functionality' '#define HAVE_ICONV' || <$tmp2.c link_check iconv 'for iconv functionality in libiconv' \ '#define HAVE_ICONV' '-liconv' link_check wctype 'for wctype functionality' '#define HAVE_WCTYPE_H' <<\! #include int main(void) { iswprint(L'c'); towupper(L'c'); return 0; } ! link_check wcwidth 'for wcwidth() ' '#define HAVE_WCWIDTH' <<\! #include int main(void) { wcwidth(L'c'); return 0; } ! link_check mbtowc 'for mbtowc()' '#define HAVE_MBTOWC' <<\! #include int main(void) { wchar_t wc; mbtowc(&wc, "x", 1); return 0; } ! link_check setlocale 'for setlocale()' '#define HAVE_SETLOCALE' <<\! #include int main(void) { setlocale(LC_ALL, ""); return 0; } ! link_check nl_langinfo 'for nl_langinfo()' '#define HAVE_NL_LANGINFO' <<\! #include int main(void) { nl_langinfo(DAY_1); return 0; } ! link_check mkstemp 'for mkstemp()' '#define HAVE_MKSTEMP' <<\! #include int main(void) { mkstemp("x"); return 0; } ! link_check fpathconf 'for fpathconf()' '#define HAVE_FPATHCONF' <<\! #include int main(void) { fpathconf(0, _PC_PATH_MAX); return 0; } ! link_check wordexp 'for wordexp()' '#define HAVE_WORDEXP' <<\! #include int main(void) { wordexp((char *)0, (wordexp_t *)0, 0); return 0; } ! compile_check arpa_inet_h 'for ' '#define HAVE_ARPA_INET_H' <<\! #include "config.h" #include #include #include #include #include ! cat >$tmp2.c <<\! #include "config.h" #include #include #include #include #ifdef HAVE_ARPA_INET_H #include #endif /* HAVE_ARPA_INET_H */ int main(void) { struct sockaddr s; socket(AF_INET, SOCK_STREAM, 0); connect(0, &s, 0); gethostbyname("foo"); return 0; } ! <$tmp2.c link_check sockets 'for socket functionality in libc' \ '#define HAVE_SOCKETS' || <$tmp2.c link_check sockets 'for socket functionality in libnsl' \ '#define HAVE_SOCKETS' '-lnsl' || <$tmp2.c link_check sockets \ 'for socket functionality in libsocket and libnsl' \ '#define HAVE_SOCKETS' '-lsocket -lnsl' #link_check ipv6 'for IPv6 functionality' '#define HAVE_IPv6_FUNCS' <<\! ##include "config.h" ##include ##include ##include ##include ##ifdef HAVE_ARPA_INET_H ##include ##endif /* HAVE_ARPA_INET_H */ # #int main(void) #{ # struct addrinfo a, *ap; # getaddrinfo("foo", "0", &a, &ap); # return 0; #} #! link_check nss 'for Network Security Services (NSS)' '#define USE_SSL #define USE_NSS' '-lsmime3 -lnss3 -lssl3 -lnspr4 -lplc4' <<\! || \ link_check openssl 'for sufficiently recent OpenSSL' \ '#define USE_SSL #define USE_OPENSSL' '-lssl -lcrypto' <<\% #include #include #include #include #include #include #include #include #include #include #include int main(void) { PR_ImportTCPSocket(0); NSS_CMSSignerInfo_AddSMIMECaps(0); return 0; } ! #include #include #include #include #include int main(void) { SSLv23_client_method(); PEM_read_PrivateKey(0, 0, 0, 0); return 0; } % if test x$have_nss = xyes then compile_check genname_h 'for genname.h' '#define HAVE_GENNAME_H' <<\! #include ! compile_check xconst_h 'for xconst.h' '#define HAVE_XCONST_H' <<\! #include ! compile_check CERTAltNameEncodedContext \ 'for CERTAltNameEncodedContext type' \ '#define HAVE_CERTAltNameEncodedContext' <<\! #include "config.h" #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_XCONST_H #include #endif #ifdef HAVE_GENNAME_H #include #endif #include CERTAltNameEncodedContext foo; ! fi if test x$have_openssl = xyes then compile_check stack_of 'for STACK_OF()' '#define HAVE_STACK_OF' <<\! #include #include #include #include #include int main(void) { STACK_OF(GENERAL_NAME) *gens = NULL; printf("%p", gens); /* to make it used */ SSLv23_client_method(); PEM_read_PrivateKey(0, 0, 0, 0); return 0; } ! fi cat >$tmp2.c <<\! #include int main(void) { gss_import_name(0, 0, GSS_C_NT_HOSTBASED_SERVICE, 0); gss_init_sec_context(0,0,0,0,0,0,0,0,0,0,0,0,0); return 0; } ! <$tmp2.c link_check gssapi 'for GSSAPI in libgss' \ '#define USE_GSSAPI' '-lgss' || <$tmp2.c link_check gssapi 'for GSSAPI in libgssapi_krb5' \ '#define USE_GSSAPI' '-lgssapi_krb5' || link_check gssapi 'for GSSAPI in libgssapi_krb5, old-style' \ '#define USE_GSSAPI #define GSSAPI_OLD_STYLE' '-lgssapi_krb5' <<\! || \ link_check gssapi 'for GSSAPI in libgssapi' \ '#define USE_GSSAPI #define GSSAPI_REG_INCLUDE' '-lgssapi' <<\% #include #include int main(void) { gss_import_name(0, 0, gss_nt_service_name, 0); gss_init_sec_context(0,0,0,0,0,0,0,0,0,0,0,0,0); return 0; } ! #include int main(void) { gss_import_name(0, 0, GSS_C_NT_HOSTBASED_SERVICE, 0); gss_init_sec_context(0,0,0,0,0,0,0,0,0,0,0,0,0); return 0; } % cat >$tmp2.c <<\! #include "config.h" #ifdef HAVE_NL_LANGINFO #include #endif : :The following optional features are enabled: #ifdef HAVE_SETLOCALE : + Locale support: Printable characters depend on the environment #if defined (HAVE_MBTOWC) && defined (HAVE_WCTYPE_H) : + Multibyte character support #endif #endif /* HAVE_SETLOCALE */ #ifdef HAVE_ICONV : + Character set conversion using iconv() #endif #if defined (HAVE_SETLOCALE) && defined (HAVE_NL_LANGINFO) && defined (CODESET) : + Automatic detection of terminal character set #endif #ifdef HAVE_SOCKETS : + Networking support (IMAP, POP3, and SMTP) #endif #ifdef USE_GSSAPI : + IMAP GSSAPI authentication #endif #if defined (USE_NSS) : + S/MIME and SSL/TLS using Network Security Services (NSS) #endif #if defined (USE_OPENSSL) : + S/MIME and SSL/TLS using OpenSSL #endif : :The following optional features are disabled: #ifndef HAVE_SETLOCALE : - Locale support: Only ASCII characters are recognized #endif #if !defined (HAVE_SETLOCALE) || !defined (HAVE_MBTOWC) || \ !defined (HAVE_WCTYPE_H) : - Multibyte character support #endif #ifndef HAVE_ICONV : - Character set conversion using iconv() #endif #if !defined (HAVE_SETLOCALE) || !defined (HAVE_NL_LANGINFO) || \ !defined (CODESET) : - Automatic detection of terminal character set #endif #ifndef HAVE_SOCKETS : - Networking support (IMAP, POP3, and SMTP) #endif #ifndef USE_GSSAPI : - IMAP GSSAPI authentication #endif #ifndef USE_SSL : - SSL/TLS (network transport authentication and encryption) #endif : :Remarks: #ifndef HAVE_SNPRINTF : * The function snprintf() could not be found. mailx will be compiled to use : sprintf() instead. This might overflow buffers if input values are larger : than expected. Use the resulting binary with care or update your system : environment and start the configuration process again. #endif #ifndef HAVE_FCHDIR : * The function fchdir() could not be found. mailx will be compiled to use : chdir() instead. This is not a problem unless the current working : directory of mailx is moved while the IMAP cache is used. #endif : ! (hash cc) && cc -E $tmp2.c | sed '/^[^:]/d; /^$/d; s/^://' >&5 exit 0 heirloom-mailx-12.5/md5.c000066400000000000000000000231471155563371200152370ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Derived from RFC 1321: */ /* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm */ /* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights reserved. License to copy and use this software is granted provided that it is identified as the "RSA Data Security, Inc. MD5 Message-Digest Algorithm" in all material mentioning or referencing this software or this function. License is also granted to make and use derivative works provided that such works are identified as "derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm" in all material mentioning or referencing the derived work. RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided "as is" without express or implied warranty of any kind. These notices must be retained in any copies of any part of this documentation and/or software. */ /* Sccsid @(#)md5.c 1.8 (gritter) 3/4/06 */ #include "rcv.h" #include "md5.h" /* * Constants for MD5Transform routine. */ #define S11 7 #define S12 12 #define S13 17 #define S14 22 #define S21 5 #define S22 9 #define S23 14 #define S24 20 #define S31 4 #define S32 11 #define S33 16 #define S34 23 #define S41 6 #define S42 10 #define S43 15 #define S44 21 static void MD5Transform(md5_type state[], unsigned char block[]); static void Encode(unsigned char *output, md5_type *input, unsigned int len); static void Decode(md5_type *output, unsigned char *input, unsigned int len); static unsigned char PADDING[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* * F, G, H and I are basic MD5 functions. */ #define F(x, y, z) ((x) & (y) | ~(x) & (z)) #define G(x, y, z) ((x) & (z) | (y) & ~(z)) #define H(x, y, z) ((x) ^ (y) ^ (z)) #define I(x, y, z) ((y) ^ ((x) | ~(z)&0xffffffff)) /* * ROTATE_LEFT rotates x left n bits. */ #define ROTATE_LEFT(x, n) ((x)<<(n) & 0xffffffff | (x) >> 32-(n)) /* * FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. * Rotation is separate from addition to prevent recomputation. */ #define FF(a, b, c, d, x, s, ac) { \ (a) = (a) + F((b), (c), (d)) + (x) + ((ac)&0xffffffff) & 0xffffffff; \ (a) = ROTATE_LEFT((a), (s)); \ (a) = (a) + (b) & 0xffffffff; \ } #define GG(a, b, c, d, x, s, ac) { \ (a) = (a) + G((b), (c), (d)) + (x) + ((ac)&0xffffffff) & 0xffffffff; \ (a) = ROTATE_LEFT((a), (s)); \ (a) = (a) + (b) & 0xffffffff; \ } #define HH(a, b, c, d, x, s, ac) { \ (a) = (a) + H((b), (c), (d)) + (x) + ((ac)&0xffffffff) & 0xffffffff; \ (a) = ROTATE_LEFT((a), (s)); \ (a) = (a) + (b) & 0xffffffff; \ } #define II(a, b, c, d, x, s, ac) { \ (a) = (a) + I((b), (c), (d)) + (x) + ((ac)&0xffffffff) & 0xffffffff; \ (a) = ROTATE_LEFT((a), (s)); \ (a) = (a) + (b) & 0xffffffff; \ } /* * MD5 initialization. Begins an MD5 operation, writing a new context. */ void MD5Init ( MD5_CTX *context /* context */ ) { context->count[0] = context->count[1] = 0; /* * Load magic initialization constants. */ context->state[0] = 0x67452301; context->state[1] = 0xefcdab89; context->state[2] = 0x98badcfe; context->state[3] = 0x10325476; } /* * MD5 block update operation. Continues an MD5 message-digest * operation, processing another message block, and updating the * context. */ void MD5Update ( MD5_CTX *context, /* context */ unsigned char *input, /* input block */ unsigned int inputLen /* length of input block */ ) { unsigned int i, index, partLen; /* Compute number of bytes mod 64 */ index = context->count[0]>>3 & 0x3F; /* Update number of bits */ if ((context->count[0] = context->count[0] + (inputLen<<3) & 0xffffffff) < (inputLen<<3 & 0xffffffff)) context->count[1] = context->count[1] + 1 & 0xffffffff; context->count[1] = context->count[1] + (inputLen>>29) & 0xffffffff; partLen = 64 - index; /* * Transform as many times as possible. */ if (inputLen >= partLen) { memcpy(&context->buffer[index], input, partLen); MD5Transform(context->state, context->buffer); for (i = partLen; i + 63 < inputLen; i += 64) MD5Transform(context->state, &input[i]); index = 0; } else i = 0; /* Buffer remaining input */ memcpy(&context->buffer[index], &input[i], inputLen-i); } /* * MD5 finalization. Ends an MD5 message-digest operation, writing the * the message digest and zeroizing the context. */ void MD5Final ( unsigned char digest[16], /* message digest */ MD5_CTX *context /* context */ ) { unsigned char bits[8]; unsigned int index, padLen; /* Save number of bits */ Encode(bits, context->count, 8); /* * Pad out to 56 mod 64. */ index = context->count[0]>>3 & 0x3f; padLen = index < 56 ? 56 - index : 120 - index; MD5Update(context, PADDING, padLen); /* Append length (before padding) */ MD5Update(context, bits, 8); /* Store state in digest */ Encode(digest, context->state, 16); /* * Zeroize sensitive information. */ memset(context, 0, sizeof *context); } /* MD5 basic transformation. Transforms state based on block. */ static void MD5Transform(md5_type state[4], unsigned char block[64]) { md5_type a = state[0], b = state[1], c = state[2], d = state[3], x[16]; Decode(x, block, 64); /* Round 1 */ FF(a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ FF(d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ FF(c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ FF(b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ FF(a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ FF(d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ FF(c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ FF(b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ FF(a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ FF(d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ /* Round 2 */ GG(a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ GG(d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ GG(b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ GG(a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */ GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ GG(b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ GG(a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ GG(c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ GG(b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ GG(d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ GG(c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ /* Round 3 */ HH(a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ HH(d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ HH(a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ HH(d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ HH(c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ HH(d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ HH(c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ HH(b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ HH(a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ HH(b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ /* Round 4 */ II(a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ II(d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ II(b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ II(d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ II(b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ II(a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ II(c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ II(a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ II(c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ II(b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ state[0] = state[0] + a & 0xffffffff; state[1] = state[1] + b & 0xffffffff; state[2] = state[2] + c & 0xffffffff; state[3] = state[3] + d & 0xffffffff; /* * Zeroize sensitive information. */ memset(x, 0, sizeof x); } /* * Encodes input (md5_type) into output (unsigned char). Assumes len is * a multiple of 4. */ static void Encode(unsigned char *output, md5_type *input, unsigned int len) { unsigned int i, j; for (i = 0, j = 0; j < len; i++, j += 4) { output[j] = input[i] & 0xff; output[j+1] = input[i]>>8 & 0xff; output[j+2] = input[i]>>16 & 0xff; output[j+3] = input[i]>> 24 & 0xff; } } /* * Decodes input (unsigned char) into output (md5_type). Assumes len is * a multiple of 4. */ static void Decode(md5_type *output, unsigned char *input, unsigned int len) { unsigned int i, j; for (i = 0, j = 0; j < len; i++, j += 4) output[i] = ((md5_type)input[j] | (md5_type)input[j+1] << 8 | (md5_type)input[j+2] << 16 | (md5_type)input[j+3] << 24) & 0xffffffff; } heirloom-mailx-12.5/md5.h000066400000000000000000000033251155563371200152400ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Derived from RFC 1321: */ /* MD5.H - header file for MD5C.C */ /* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights reserved. License to copy and use this software is granted provided that it is identified as the "RSA Data Security, Inc. MD5 Message-Digest Algorithm" in all material mentioning or referencing this software or this function. License is also granted to make and use derivative works provided that such works are identified as "derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm" in all material mentioning or referencing the derived work. RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided "as is" without express or implied warranty of any kind. These notices must be retained in any copies of any part of this documentation and/or software. */ /* Sccsid @(#)md5.h 1.8 (gritter) 3/4/06 */ /* * This version of MD5 has been changed such that any unsigned type with * at least 32 bits is acceptable. This is important e.g. for Cray vector * machines which provide only 64-bit integers. */ typedef unsigned long md5_type; typedef struct { md5_type state[4]; /* state (ABCD) */ md5_type count[2]; /* number of bits, modulo 2^64 (lsb first) */ unsigned char buffer[64]; /* input buffer */ } MD5_CTX; void MD5Init(MD5_CTX *); void MD5Update(MD5_CTX *, unsigned char *, unsigned int); void MD5Final(unsigned char[16], MD5_CTX *); void hmac_md5(unsigned char *, int, unsigned char *, int, void *); heirloom-mailx-12.5/mime.c000066400000000000000000001060131155563371200154730ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 2000 * Gunnar Ritter. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Gunnar Ritter * and his contributors. * 4. Neither the name of Gunnar Ritter nor the names of his contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char copyright[] = "@(#) Copyright (c) 2000, 2002 Gunnar Ritter. All rights reserved.\n"; static char sccsid[] = "@(#)mime.c 2.70 (gritter) 3/10/09"; #endif /* DOSCCS */ #endif /* not lint */ #include "rcv.h" #include "extern.h" #include #include #ifdef HAVE_WCTYPE_H #include #endif /* HAVE_WCTYPE_H */ /* * Mail -- a mail program * * MIME support functions. */ /* * You won't guess what these are for. */ static const char basetable[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; static char *mimetypes_world = "/etc/mime.types"; static char *mimetypes_user = "~/.mime.types"; char *us_ascii = "us-ascii"; static int mustquote_body(int c); static int mustquote_hdr(const char *cp, int wordstart, int wordend); static int mustquote_inhdrq(int c); static size_t delctrl(char *cp, size_t sz); static char *getcharset(int isclean); static int has_highbit(register const char *s); #ifdef HAVE_ICONV static void uppercopy(char *dest, const char *src); static void stripdash(char *p); static size_t iconv_ft(iconv_t cd, char **inb, size_t *inbleft, char **outb, size_t *outbleft); static void invalid_seq(int c); #endif /* HAVE_ICONV */ static int is_this_enc(const char *line, const char *encoding); static char *mime_tline(char *x, char *l); static char *mime_type(char *ext, char *filename); static enum mimeclean mime_isclean(FILE *f); static enum conversion gettextconversion(void); static char *ctohex(int c, char *hex); static size_t mime_write_toqp(struct str *in, FILE *fo, int (*mustquote)(int)); static void mime_str_toqp(struct str *in, struct str *out, int (*mustquote)(int), int inhdr); static void mime_fromqp(struct str *in, struct str *out, int ishdr); static size_t mime_write_tohdr(struct str *in, FILE *fo); static size_t convhdra(char *str, size_t len, FILE *fp); static size_t mime_write_tohdr_a(struct str *in, FILE *f); static void addstr(char **buf, size_t *sz, size_t *pos, char *str, size_t len); static void addconv(char **buf, size_t *sz, size_t *pos, char *str, size_t len); static size_t fwrite_td(void *ptr, size_t size, size_t nmemb, FILE *f, enum tdflags flags, char *prefix, size_t prefixlen); /* * Check if c must be quoted inside a message's body. */ static int mustquote_body(int c) { if (c != '\n' && (c < 040 || c == '=' || c >= 0177)) return 1; return 0; } /* * Check if c must be quoted inside a message's header. */ static int mustquote_hdr(const char *cp, int wordstart, int wordend) { int c = *cp & 0377; if (c != '\n' && (c < 040 || c >= 0177)) return 1; if (wordstart && cp[0] == '=' && cp[1] == '?') return 1; if (cp[0] == '?' && cp[1] == '=' && (wordend || cp[2] == '\0' || whitechar(cp[2]&0377))) return 1; return 0; } /* * Check if c must be quoted inside a quoting in a message's header. */ static int mustquote_inhdrq(int c) { if (c != '\n' && (c <= 040 || c == '=' || c == '?' || c == '_' || c >= 0177)) return 1; return 0; } static size_t delctrl(char *cp, size_t sz) { size_t x = 0, y = 0; while (x < sz) { if (!cntrlchar(cp[x]&0377)) cp[y++] = cp[x]; x++; } return y; } /* * Check if a name's address part contains invalid characters. */ int mime_name_invalid(char *name, int putmsg) { char *addr, *p; int in_quote = 0, in_domain = 0, err = 0, hadat = 0; if (is_fileaddr(name)) return 0; addr = skin(name); if (addr == NULL || *addr == '\0') return 1; for (p = addr; *p != '\0'; p++) { if (*p == '\"') { in_quote = !in_quote; } else if (*p < 040 || (*p & 0377) >= 0177) { err = *p & 0377; break; } else if (in_domain == 2) { if ((*p == ']' && p[1] != '\0') || *p == '\0' || *p == '\\' || whitechar(*p & 0377)) { err = *p & 0377; break; } } else if (in_quote && in_domain == 0) { /*EMPTY*/; } else if (*p == '\\' && p[1] != '\0') { p++; } else if (*p == '@') { if (hadat++) { if (putmsg) { fprintf(stderr, catgets(catd, CATSET, 142, "%s contains invalid @@ sequence\n"), addr); putmsg = 0; } err = *p; break; } if (p[1] == '[') in_domain = 2; else in_domain = 1; continue; } else if (*p == '(' || *p == ')' || *p == '<' || *p == '>' || *p == ',' || *p == ';' || *p == ':' || *p == '\\' || *p == '[' || *p == ']') { err = *p & 0377; break; } hadat = 0; } if (err && putmsg) { fprintf(stderr, catgets(catd, CATSET, 143, "%s contains invalid character '"), addr); #ifdef HAVE_SETLOCALE if (isprint(err)) #else /* !HAVE_SETLOCALE */ if (err >= 040 && err <= 0177) #endif /* !HAVE_SETLOCALE */ putc(err, stderr); else fprintf(stderr, "\\%03o", err); fprintf(stderr, catgets(catd, CATSET, 144, "'\n")); } return err; } /* * Check all addresses in np and delete invalid ones. */ struct name * checkaddrs(struct name *np) { struct name *n = np; while (n != NULL) { if (mime_name_invalid(n->n_name, 1)) { if (n->n_blink) n->n_blink->n_flink = n->n_flink; if (n->n_flink) n->n_flink->n_blink = n->n_blink; if (n == np) np = n->n_flink; } n = n->n_flink; } return np; } static char defcharset[] = "utf-8"; /* * Get the character set dependant on the conversion. */ static char * getcharset(int isclean) { char *charset; if (isclean & (MIME_CTRLCHAR|MIME_HASNUL)) charset = NULL; else if (isclean & MIME_HIGHBIT) { charset = (wantcharset && wantcharset != (char *)-1) ? wantcharset : value("charset"); if (charset == NULL) { charset = defcharset; } } else { /* * This variable shall remain undocumented because * only experts should change it. */ charset = value("charset7"); if (charset == NULL) { charset = us_ascii; } } return charset; } /* * Get the setting of the terminal's character set. */ char * gettcharset(void) { char *t; if ((t = value("ttycharset")) == NULL) if ((t = value("charset")) == NULL) t = defcharset; return t; } static int has_highbit(const char *s) { if (s) { do if (*s & 0200) return 1; while (*s++ != '\0'); } return 0; } static int name_highbit(struct name *np) { while (np) { if (has_highbit(np->n_name) || has_highbit(np->n_fullname)) return 1; np = np->n_flink; } return 0; } char * need_hdrconv(struct header *hp, enum gfield w) { if (w & GIDENT) { if (hp->h_from && name_highbit(hp->h_from)) goto needs; else if (has_highbit(myaddrs(hp))) goto needs; if (hp->h_organization && has_highbit(hp->h_organization)) goto needs; else if (has_highbit(value("ORGANIZATION"))) goto needs; if (hp->h_replyto && name_highbit(hp->h_replyto)) goto needs; else if (has_highbit(value("replyto"))) goto needs; if (hp->h_sender && name_highbit(hp->h_sender)) goto needs; else if (has_highbit(value("sender"))) goto needs; } if (w & GTO && name_highbit(hp->h_to)) goto needs; if (w & GCC && name_highbit(hp->h_cc)) goto needs; if (w & GBCC && name_highbit(hp->h_bcc)) goto needs; if (w & GSUBJECT && has_highbit(hp->h_subject)) goto needs; return NULL; needs: return getcharset(MIME_HIGHBIT); } #ifdef HAVE_ICONV /* * Convert a string, upper-casing the characters. */ static void uppercopy(char *dest, const char *src) { do *dest++ = upperconv(*src & 0377); while (*src++); } /* * Strip dashes. */ static void stripdash(char *p) { char *q = p; do if (*(q = p) != '-') q++; while (*p++); } /* * An iconv_open wrapper that tries to convert between character set * naming conventions. */ iconv_t iconv_open_ft(const char *tocode, const char *fromcode) { iconv_t id; char *t, *f; /* * On Linux systems, this call may succeed. */ if ((id = iconv_open(tocode, fromcode)) != (iconv_t)-1) return id; /* * Remove the "iso-" prefixes for Solaris. */ if (ascncasecmp(tocode, "iso-", 4) == 0) tocode += 4; else if (ascncasecmp(tocode, "iso", 3) == 0) tocode += 3; if (ascncasecmp(fromcode, "iso-", 4) == 0) fromcode += 4; else if (ascncasecmp(fromcode, "iso", 3) == 0) fromcode += 3; if (*tocode == '\0' || *fromcode == '\0') return (iconv_t) -1; if ((id = iconv_open(tocode, fromcode)) != (iconv_t)-1) return id; /* * Solaris prefers upper-case charset names. Don't ask... */ t = salloc(strlen(tocode) + 1); uppercopy(t, tocode); f = salloc(strlen(fromcode) + 1); uppercopy(f, fromcode); if ((id = iconv_open(t, f)) != (iconv_t)-1) return id; /* * Strip dashes for UnixWare. */ stripdash(t); stripdash(f); if ((id = iconv_open(t, f)) != (iconv_t)-1) return id; /* * Add your vendor's sillynesses here. */ /* * If the encoding names are equal at this point, they * are just not understood by iconv(), and we cannot * sensibly use it in any way. We do not perform this * as an optimization above since iconv() can otherwise * be used to check the validity of the input even with * identical encoding names. */ if (strcmp(t, f) == 0) errno = 0; return (iconv_t)-1; } /* * Fault-tolerant iconv() function. */ static size_t iconv_ft(iconv_t cd, char **inb, size_t *inbleft, char **outb, size_t *outbleft) { size_t sz = 0; while ((sz = iconv(cd, inb, inbleft, outb, outbleft)) == (size_t)-1 && (errno == EILSEQ || errno == EINVAL)) { if (*inbleft > 0) { (*inb)++; (*inbleft)--; } else { **outb = '\0'; break; } if (*outbleft > 0) { *(*outb)++ = '?'; (*outbleft)--; } else { **outb = '\0'; break; } } return sz; } /* * Print an error because of an invalid character sequence. */ /*ARGSUSED*/ static void invalid_seq(int c) { /*fprintf(stderr, "iconv: cannot convert %c\n", c);*/ } #endif /* HAVE_ICONV */ static int is_this_enc(const char *line, const char *encoding) { int quoted = 0, c; if (*line == '"') quoted = 1, line++; while (*line && *encoding) if (c = *line++, lowerconv(c) != *encoding++) return 0; if (quoted && *line == '"') return 1; if (*line == '\0' || whitechar(*line & 0377)) return 1; return 0; } /* * Get the mime encoding from a Content-Transfer-Encoding header field. */ enum mimeenc mime_getenc(char *p) { if (is_this_enc(p, "7bit")) return MIME_7B; if (is_this_enc(p, "8bit")) return MIME_8B; if (is_this_enc(p, "base64")) return MIME_B64; if (is_this_enc(p, "binary")) return MIME_BIN; if (is_this_enc(p, "quoted-printable")) return MIME_QP; return MIME_NONE; } /* * Get the mime content from a Content-Type header field, other parameters * already stripped. */ int mime_getcontent(char *s) { if (strchr(s, '/') == NULL) /* for compatibility with non-MIME */ return MIME_TEXT; if (asccasecmp(s, "text/plain") == 0) return MIME_TEXT_PLAIN; if (asccasecmp(s, "text/html") == 0) return MIME_TEXT_HTML; if (ascncasecmp(s, "text/", 5) == 0) return MIME_TEXT; if (asccasecmp(s, "message/rfc822") == 0) return MIME_822; if (ascncasecmp(s, "message/", 8) == 0) return MIME_MESSAGE; if (asccasecmp(s, "multipart/alternative") == 0) return MIME_ALTERNATIVE; if (asccasecmp(s, "multipart/digest") == 0) return MIME_DIGEST; if (ascncasecmp(s, "multipart/", 10) == 0) return MIME_MULTI; if (asccasecmp(s, "application/x-pkcs7-mime") == 0 || asccasecmp(s, "application/pkcs7-mime") == 0) return MIME_PKCS7; return MIME_UNKNOWN; } /* * Get a mime style parameter from a header line. */ char * mime_getparam(char *param, char *h) { char *p = h, *q, *r; int c; size_t sz; sz = strlen(param); if (!whitechar(*p & 0377)) { c = '\0'; while (*p && (*p != ';' || c == '\\')) { c = c == '\\' ? '\0' : *p; p++; } if (*p++ == '\0') return NULL; } for (;;) { while (whitechar(*p & 0377)) p++; if (ascncasecmp(p, param, sz) == 0) { p += sz; while (whitechar(*p & 0377)) p++; if (*p++ == '=') break; } c = '\0'; while (*p && (*p != ';' || c == '\\')) { if (*p == '"' && c != '\\') { p++; while (*p && (*p != '"' || c == '\\')) { c = c == '\\' ? '\0' : *p; p++; } p++; } else { c = c == '\\' ? '\0' : *p; p++; } } if (*p++ == '\0') return NULL; } while (whitechar(*p & 0377)) p++; q = p; c = '\0'; if (*p == '"') { p++; if ((q = strchr(p, '"')) == NULL) return NULL; } else { q = p; while (*q && !whitechar(*q & 0377) && *q != ';') q++; } sz = q - p; r = salloc(q - p + 1); memcpy(r, p, sz); *(r + sz) = '\0'; return r; } /* * Get the boundary out of a Content-Type: multipart/xyz header field. */ char * mime_getboundary(char *h) { char *p, *q; size_t sz; if ((p = mime_getparam("boundary", h)) == NULL) return NULL; sz = strlen(p); q = salloc(sz + 3); memcpy(q, "--", 2); memcpy(q + 2, p, sz); *(q + sz + 2) = '\0'; return q; } /* * Get a line like "text/html html" and look if x matches the extension. */ static char * mime_tline(char *x, char *l) { char *type, *n; int match = 0; if ((*l & 0200) || alphachar(*l & 0377) == 0) return NULL; type = l; while (blankchar(*l & 0377) == 0 && *l != '\0') l++; if (*l == '\0') return NULL; *l++ = '\0'; while (blankchar(*l & 0377) != 0 && *l != '\0') l++; if (*l == '\0') return NULL; while (*l != '\0') { n = l; while (whitechar(*l & 0377) == 0 && *l != '\0') l++; if (*l != '\0') *l++ = '\0'; if (strcmp(x, n) == 0) { match = 1; break; } while (whitechar(*l & 0377) != 0 && *l != '\0') l++; } if (match != 0) { n = salloc(strlen(type) + 1); strcpy(n, type); return n; } return NULL; } /* * Check the given MIME type file for extension ext. */ static char * mime_type(char *ext, char *filename) { FILE *f; char *line = NULL; size_t linesize = 0; char *type = NULL; if ((f = Fopen(filename, "r")) == NULL) return NULL; while (fgetline(&line, &linesize, NULL, NULL, f, 0)) { if ((type = mime_tline(ext, line)) != NULL) break; } Fclose(f); if (line) free(line); return type; } /* * Return the Content-Type matching the extension of name. */ char * mime_filecontent(char *name) { char *ext, *content; if ((ext = strrchr(name, '.')) == NULL || *++ext == '\0') return NULL; if ((content = mime_type(ext, expand(mimetypes_user))) != NULL) return content; if ((content = mime_type(ext, mimetypes_world)) != NULL) return content; return NULL; } /* * Check file contents. */ static enum mimeclean mime_isclean(FILE *f) { long initial_pos; unsigned curlen = 1, maxlen = 0, limit = 950; enum mimeclean isclean = 0; char *cp; int c = EOF, lastc; initial_pos = ftell(f); do { lastc = c; c = getc(f); curlen++; if (c == '\n' || c == EOF) { /* * RFC 821 imposes a maximum line length of 1000 * characters including the terminating CRLF * sequence. The configurable limit must not * exceed that including a safety zone. */ if (curlen > maxlen) maxlen = curlen; curlen = 1; } else if (c & 0200) { isclean |= MIME_HIGHBIT; } else if (c == '\0') { isclean |= MIME_HASNUL; break; } else if ((c < 040 && (c != '\t' && c != '\f')) || c == 0177) { isclean |= MIME_CTRLCHAR; } } while (c != EOF); if (lastc != '\n') isclean |= MIME_NOTERMNL; clearerr(f); fseek(f, initial_pos, SEEK_SET); if ((cp = value("maximum-unencoded-line-length")) != NULL) limit = atoi(cp); if (limit < 0 || limit > 950) limit = 950; if (maxlen > limit) isclean |= MIME_LONGLINES; return isclean; } /* * Get the conversion that matches the encoding specified in the environment. */ static enum conversion gettextconversion(void) { char *p; int convert; if ((p = value("encoding")) == NULL) return CONV_8BIT; if (equal(p, "quoted-printable")) convert = CONV_TOQP; else if (equal(p, "8bit")) convert = CONV_8BIT; else { fprintf(stderr, catgets(catd, CATSET, 177, "Warning: invalid encoding %s, using 8bit\n"), p); convert = CONV_8BIT; } return convert; } int get_mime_convert(FILE *fp, char **contenttype, char **charset, enum mimeclean *isclean, int dosign) { int convert; *isclean = mime_isclean(fp); if (*isclean & MIME_HASNUL || *contenttype && ascncasecmp(*contenttype, "text/", 5) || *contenttype == NULL && *isclean & MIME_CTRLCHAR) { convert = CONV_TOB64; if (*contenttype == NULL || ascncasecmp(*contenttype, "text/", 5) == 0) *contenttype = "application/octet-stream"; *charset = NULL; } else if (*isclean & (MIME_LONGLINES|MIME_CTRLCHAR|MIME_NOTERMNL) || dosign) convert = CONV_TOQP; else if (*isclean & MIME_HIGHBIT) convert = gettextconversion(); else convert = CONV_7BIT; if (*contenttype == NULL || ascncasecmp(*contenttype, "text/", 5) == 0) { *charset = getcharset(*isclean); if (wantcharset == (char *)-1) { *contenttype = "application/octet-stream"; *charset = NULL; } if (*isclean & MIME_CTRLCHAR) { /* * RFC 2046 forbids control characters other than * ^I or ^L in text/plain bodies. However, some * obscure character sets actually contain these * characters, so the content type can be set. */ if ((*contenttype = value("contenttype-cntrl")) == NULL) *contenttype = "application/octet-stream"; } else if (*contenttype == NULL) *contenttype = "text/plain"; } return convert; } /* * Convert c to a hexadecimal character string and store it in hex. */ static char * ctohex(int c, char *hex) { unsigned char d; hex[2] = '\0'; d = c % 16; hex[1] = basetable[d]; if (c > d) hex[0] = basetable[(c - d) / 16]; else hex[0] = basetable[0]; return hex; } /* * Write to a file converting to quoted-printable. * The mustquote function determines whether a character must be quoted. */ static size_t mime_write_toqp(struct str *in, FILE *fo, int (*mustquote)(int)) { char *p, *upper, *h, hex[3]; int l; size_t sz; sz = in->l; upper = in->s + in->l; for (p = in->s, l = 0; p < upper; p++) { if (mustquote(*p&0377) || p < upper-1 && p[1] == '\n' && blankchar(p[0]&0377) || p < upper-4 && l == 0 && p[0] == 'F' && p[1] == 'r' && p[2] == 'o' && p[3] == 'm' || *p == '.' && l == 0 && p < upper-1 && p[1] == '\n') { if (l >= 69) { sz += 2; fwrite("=\n", sizeof (char), 2, fo); l = 0; } sz += 2; putc('=', fo); h = ctohex(*p&0377, hex); fwrite(h, sizeof *h, 2, fo); l += 3; } else { if (*p == '\n') l = 0; else if (l >= 71) { sz += 2; fwrite("=\n", sizeof (char), 2, fo); l = 0; } putc(*p, fo); l++; } } return sz; } /* * Write to a stringstruct converting to quoted-printable. * The mustquote function determines whether a character must be quoted. */ static void mime_str_toqp(struct str *in, struct str *out, int (*mustquote)(int), int inhdr) { char *p, *q, *upper; out->s = smalloc(in->l * 3 + 1); q = out->s; out->l = in->l; upper = in->s + in->l; for (p = in->s; p < upper; p++) { if (mustquote(*p&0377) || p+1 < upper && *(p + 1) == '\n' && blankchar(*p & 0377)) { if (inhdr && *p == ' ') { *q++ = '_'; } else { out->l += 2; *q++ = '='; ctohex(*p&0377, q); q += 2; } } else { *q++ = *p; } } *q = '\0'; } /* * Write to a stringstruct converting from quoted-printable. */ static void mime_fromqp(struct str *in, struct str *out, int ishdr) { char *p, *q, *upper; char quote[4]; out->l = in->l; out->s = smalloc(out->l + 1); upper = in->s + in->l; for (p = in->s, q = out->s; p < upper; p++) { if (*p == '=') { do { p++; out->l--; } while (blankchar(*p & 0377) && p < upper); if (p == upper) break; if (*p == '\n') { out->l--; continue; } if (p + 1 >= upper) break; quote[0] = *p++; quote[1] = *p; quote[2] = '\0'; *q = (char)strtol(quote, NULL, 16); q++; out->l--; } else if (ishdr && *p == '_') *q++ = ' '; else *q++ = *p; } return; } #define mime_fromhdr_inc(inc) { \ size_t diff = q - out->s; \ out->s = srealloc(out->s, (maxstor += inc) + 1); \ q = &(out->s)[diff]; \ } /* * Convert header fields from RFC 1522 format */ void mime_fromhdr(struct str *in, struct str *out, enum tdflags flags) { char *p, *q, *op, *upper, *cs, *cbeg, *tcs, *lastwordend = NULL; struct str cin, cout; int convert; size_t maxstor, lastoutl = 0; #ifdef HAVE_ICONV iconv_t fhicd = (iconv_t)-1; #endif tcs = gettcharset(); maxstor = in->l; out->s = smalloc(maxstor + 1); out->l = 0; upper = in->s + in->l; for (p = in->s, q = out->s; p < upper; p++) { op = p; if (*p == '=' && *(p + 1) == '?') { p += 2; cbeg = p; while (p < upper && *p != '?') p++; /* strip charset */ if (p >= upper) goto notmime; cs = salloc(++p - cbeg); memcpy(cs, cbeg, p - cbeg - 1); cs[p - cbeg - 1] = '\0'; #ifdef HAVE_ICONV if (fhicd != (iconv_t)-1) iconv_close(fhicd); if (strcmp(cs, tcs)) fhicd = iconv_open_ft(tcs, cs); else fhicd = (iconv_t)-1; #endif switch (*p) { case 'B': case 'b': convert = CONV_FROMB64; break; case 'Q': case 'q': convert = CONV_FROMQP; break; default: /* invalid, ignore */ goto notmime; } if (*++p != '?') goto notmime; cin.s = ++p; cin.l = 1; for (;;) { if (p == upper) goto fromhdr_end; if (*p++ == '?' && *p == '=') break; cin.l++; } cin.l--; switch (convert) { case CONV_FROMB64: mime_fromb64(&cin, &cout, 1); break; case CONV_FROMQP: mime_fromqp(&cin, &cout, 1); break; } if (lastwordend) { q = lastwordend; out->l = lastoutl; } #ifdef HAVE_ICONV if ((flags & TD_ICONV) && fhicd != (iconv_t)-1) { char *iptr, *mptr, *nptr, *uptr; size_t inleft, outleft; again: inleft = cout.l; outleft = maxstor - out->l; mptr = nptr = q; uptr = nptr + outleft; iptr = cout.s; if (iconv_ft(fhicd, &iptr, &inleft, &nptr, &outleft) == (size_t)-1 && errno == E2BIG) { iconv(fhicd, NULL, NULL, NULL, NULL); mime_fromhdr_inc(inleft); goto again; } /* * For state-dependent encodings, * reset the state here, assuming * that states are restricted to * single encoded-word parts. */ while (iconv(fhicd, NULL, NULL, &nptr, &outleft) == (size_t)-1 && errno == E2BIG) mime_fromhdr_inc(16); out->l += uptr - mptr - outleft; q += uptr - mptr - outleft; } else { #endif while (cout.l > maxstor - out->l) mime_fromhdr_inc(cout.l - (maxstor - out->l)); memcpy(q, cout.s, cout.l); q += cout.l; out->l += cout.l; #ifdef HAVE_ICONV } #endif free(cout.s); lastwordend = q; lastoutl = out->l; } else { notmime: p = op; while (out->l >= maxstor) mime_fromhdr_inc(16); *q++ = *p; out->l++; if (!blankchar(*p&0377)) lastwordend = NULL; } } fromhdr_end: *q = '\0'; if (flags & TD_ISPR) { struct str new; makeprint(out, &new); free(out->s); *out = new; } if (flags & TD_DELCTRL) out->l = delctrl(out->s, out->l); #ifdef HAVE_ICONV if (fhicd != (iconv_t)-1) iconv_close(fhicd); #endif return; } /* * Convert header fields to RFC 1522 format and write to the file fo. */ static size_t mime_write_tohdr(struct str *in, FILE *fo) { char *upper, *wbeg, *wend, *charset, *lastwordend = NULL, *lastspc, b, *charset7; struct str cin, cout; size_t sz = 0, col = 0, wr, charsetlen, charset7len; int quoteany, mustquote, broken, maxcol = 65 /* there is the header field's name, too */; upper = in->s + in->l; charset = getcharset(MIME_HIGHBIT); if ((charset7 = value("charset7")) == NULL) charset7 = us_ascii; charsetlen = strlen(charset); charset7len = strlen(charset7); charsetlen = smax(charsetlen, charset7len); b = 0; for (wbeg = in->s, quoteany = 0; wbeg < upper; wbeg++) { b |= *wbeg; if (mustquote_hdr(wbeg, wbeg == in->s, wbeg == &upper[-1])) quoteany++; } if (2 * quoteany > in->l) { /* * Print the entire field in base64. */ for (wbeg = in->s; wbeg < upper; wbeg = wend) { wend = upper; cin.s = wbeg; for (;;) { cin.l = wend - wbeg; if (cin.l * 4/3 + 7 + charsetlen < maxcol - col) { fprintf(fo, "=?%s?B?", b&0200 ? charset : charset7); wr = mime_write_tob64(&cin, fo, 1); fwrite("?=", sizeof (char), 2, fo); wr += 7 + charsetlen; sz += wr, col += wr; if (wend < upper) { fwrite("\n ", sizeof (char), 2, fo); sz += 2; col = 0; maxcol = 76; } break; } else { if (col) { fprintf(fo, "\n "); sz += 2; col = 0; maxcol = 76; } else wend -= 4; } } } } else { /* * Print the field word-wise in quoted-printable. */ broken = 0; for (wbeg = in->s; wbeg < upper; wbeg = wend) { lastspc = NULL; while (wbeg < upper && whitechar(*wbeg & 0377)) { lastspc = lastspc ? lastspc : wbeg; wbeg++; col++; broken = 0; } if (wbeg == upper) { if (lastspc) while (lastspc < wbeg) { putc(*lastspc&0377, fo); lastspc++, sz++; } break; } mustquote = 0; b = 0; for (wend = wbeg; wend < upper && !whitechar(*wend & 0377); wend++) { b |= *wend; if (mustquote_hdr(wend, wend == wbeg, wbeg == &upper[-1])) mustquote++; } if (mustquote || broken || (wend - wbeg) >= 74 && quoteany) { for (;;) { cin.s = lastwordend ? lastwordend : wbeg; cin.l = wend - cin.s; mime_str_toqp(&cin, &cout, mustquote_inhdrq, 1); if ((wr = cout.l + charsetlen + 7) < maxcol - col) { if (lastspc) while (lastspc < wbeg) { putc(*lastspc &0377, fo); lastspc++, sz++; } fprintf(fo, "=?%s?Q?", b&0200 ? charset : charset7); fwrite(cout.s, sizeof *cout.s, cout.l, fo); fwrite("?=", 1, 2, fo); sz += wr, col += wr; free(cout.s); break; } else { broken = 1; if (col) { putc('\n', fo); sz++; col = 0; maxcol = 76; if (lastspc == NULL) { putc(' ', fo); sz++; maxcol--; } else maxcol -= wbeg - lastspc; } else { wend -= 4; } free(cout.s); } } lastwordend = wend; } else { if (col && wend - wbeg > maxcol - col) { putc('\n', fo); sz++; col = 0; maxcol = 76; if (lastspc == NULL) { putc(' ', fo); sz++; maxcol--; } else maxcol -= wbeg - lastspc; } if (lastspc) while (lastspc < wbeg) { putc(*lastspc&0377, fo); lastspc++, sz++; } wr = fwrite(wbeg, sizeof *wbeg, wend - wbeg, fo); sz += wr, col += wr; lastwordend = NULL; } } } return sz; } /* * Write len characters of the passed string to the passed file, * doing charset and header conversion. */ static size_t convhdra(char *str, size_t len, FILE *fp) { #ifdef HAVE_ICONV char *ip, *op; size_t isz, osz; #endif struct str cin; size_t cbufsz; char *cbuf; size_t sz; cbuf = ac_alloc(cbufsz = 1); #ifdef HAVE_ICONV if (iconvd == (iconv_t)-1) { #endif cin.s = str; cin.l = len; #ifdef HAVE_ICONV } else { again: ip = str; isz = len; op = cbuf; osz = cbufsz; if (iconv(iconvd, &ip, &isz, &op, &osz) == (size_t)-1) { if (errno != E2BIG) { ac_free(cbuf); return 0; } cbuf = ac_alloc(cbufsz += isz); goto again; } cin.s = cbuf; cin.l = cbufsz - osz; } #endif /* HAVE_ICONV */ sz = mime_write_tohdr(&cin, fp); ac_free(cbuf); return sz; } /* * Write an address to a header field. */ static size_t mime_write_tohdr_a(struct str *in, FILE *f) { char *cp, *lastcp; size_t sz = 0; in->s[in->l] = '\0'; lastcp = in->s; if ((cp = routeaddr(in->s)) != NULL && cp > lastcp) { sz += convhdra(lastcp, cp - lastcp, f); lastcp = cp; } else cp = in->s; for ( ; *cp; cp++) { switch (*cp) { case '(': sz += fwrite(lastcp, 1, cp - lastcp + 1, f); lastcp = ++cp; cp = skip_comment(cp); if (--cp > lastcp) sz += convhdra(lastcp, cp - lastcp, f); lastcp = cp; break; case '"': while (*cp) { if (*++cp == '"') break; if (*cp == '\\' && cp[1]) cp++; } break; } } if (cp > lastcp) sz += fwrite(lastcp, 1, cp - lastcp, f); return sz; } static void addstr(char **buf, size_t *sz, size_t *pos, char *str, size_t len) { *buf = srealloc(*buf, *sz += len); memcpy(&(*buf)[*pos], str, len); *pos += len; } static void addconv(char **buf, size_t *sz, size_t *pos, char *str, size_t len) { struct str in, out; in.s = str; in.l = len; mime_fromhdr(&in, &out, TD_ISPR|TD_ICONV); addstr(buf, sz, pos, out.s, out.l); free(out.s); } /* * Interpret MIME strings in parts of an address field. */ char * mime_fromaddr(char *name) { char *cp, *lastcp; char *res = NULL; size_t ressz = 1, rescur = 0; if (name == NULL || *name == '\0') return name; if ((cp = routeaddr(name)) != NULL && cp > name) { addconv(&res, &ressz, &rescur, name, cp - name); lastcp = cp; } else cp = lastcp = name; for ( ; *cp; cp++) { switch (*cp) { case '(': addstr(&res, &ressz, &rescur, lastcp, cp - lastcp + 1); lastcp = ++cp; cp = skip_comment(cp); if (--cp > lastcp) addconv(&res, &ressz, &rescur, lastcp, cp - lastcp); lastcp = cp; break; case '"': while (*cp) { if (*++cp == '"') break; if (*cp == '\\' && cp[1]) cp++; } break; } } if (cp > lastcp) addstr(&res, &ressz, &rescur, lastcp, cp - lastcp); res[rescur] = '\0'; cp = savestr(res); free(res); return cp; } /* * fwrite whilst adding prefix, if present. */ size_t prefixwrite(void *ptr, size_t size, size_t nmemb, FILE *f, char *prefix, size_t prefixlen) { static FILE *lastf; static char lastc = '\n'; size_t rsz, wsz = 0; char *p = ptr; if (nmemb == 0) return 0; if (prefix == NULL) { lastf = f; lastc = ((char *)ptr)[size * nmemb - 1]; return fwrite(ptr, size, nmemb, f); } if (f != lastf || lastc == '\n') { if (*p == '\n' || *p == '\0') wsz += fwrite(prefix, sizeof *prefix, prefixlen, f); else { fputs(prefix, f); wsz += strlen(prefix); } } lastf = f; for (rsz = size * nmemb; rsz; rsz--, p++, wsz++) { putc(*p, f); if (*p != '\n' || rsz == 1) { continue; } if (p[1] == '\n' || p[1] == '\0') wsz += fwrite(prefix, sizeof *prefix, prefixlen, f); else { fputs(prefix, f); wsz += strlen(prefix); } } lastc = p[-1]; return wsz; } /* * fwrite while checking for displayability. */ static size_t fwrite_td(void *ptr, size_t size, size_t nmemb, FILE *f, enum tdflags flags, char *prefix, size_t prefixlen) { char *upper; size_t sz, csize; #ifdef HAVE_ICONV char *iptr, *nptr; size_t inleft, outleft; #endif char *mptr, *xmptr, *mlptr = NULL; size_t mptrsz; csize = size * nmemb; mptrsz = csize; mptr = xmptr = ac_alloc(mptrsz + 1); #ifdef HAVE_ICONV if ((flags & TD_ICONV) && iconvd != (iconv_t)-1) { again: inleft = csize; outleft = mptrsz; nptr = mptr; iptr = ptr; if (iconv_ft(iconvd, &iptr, &inleft, &nptr, &outleft) == (size_t)-1 && errno == E2BIG) { iconv(iconvd, NULL, NULL, NULL, NULL); ac_free(mptr); mptrsz += inleft; mptr = ac_alloc(mptrsz + 1); goto again; } nmemb = mptrsz - outleft; size = sizeof (char); ptr = mptr; csize = size * nmemb; } else #endif { memcpy(mptr, ptr, csize); } upper = mptr + csize; *upper = '\0'; if (flags & TD_ISPR) { struct str in, out; in.s = mptr; in.l = csize; makeprint(&in, &out); mptr = mlptr = out.s; csize = out.l; } if (flags & TD_DELCTRL) csize = delctrl(mptr, csize); sz = prefixwrite(mptr, sizeof *mptr, csize, f, prefix, prefixlen); ac_free(xmptr); free(mlptr); return sz; } /* * fwrite performing the given MIME conversion. */ size_t mime_write(void *ptr, size_t size, FILE *f, enum conversion convert, enum tdflags dflags, char *prefix, size_t prefixlen, char **restp, size_t *restsizep) { struct str in, out; size_t sz, csize; int is_text = 0; #ifdef HAVE_ICONV char mptr[LINESIZE * 6]; char *iptr, *nptr; size_t inleft, outleft; #endif if (size == 0) return 0; csize = size; #ifdef HAVE_ICONV if (csize < sizeof mptr && (dflags & TD_ICONV) && iconvd != (iconv_t)-1 && (convert == CONV_TOQP || convert == CONV_8BIT || convert == CONV_TOB64 || convert == CONV_TOHDR)) { inleft = csize; outleft = sizeof mptr; nptr = mptr; iptr = ptr; if (iconv(iconvd, &iptr, &inleft, &nptr, &outleft) != (size_t)-1) { in.l = sizeof mptr - outleft; in.s = mptr; } else { if (errno == EILSEQ || errno == EINVAL) invalid_seq(*iptr); return 0; } } else { #endif in.s = ptr; in.l = csize; #ifdef HAVE_ICONV } #endif switch (convert) { case CONV_FROMQP: mime_fromqp(&in, &out, 0); sz = fwrite_td(out.s, sizeof *out.s, out.l, f, dflags, prefix, prefixlen); free(out.s); break; case CONV_TOQP: sz = mime_write_toqp(&in, f, mustquote_body); break; case CONV_8BIT: sz = prefixwrite(in.s, sizeof *in.s, in.l, f, prefix, prefixlen); break; case CONV_FROMB64_T: is_text = 1; /*FALLTHROUGH*/ case CONV_FROMB64: mime_fromb64_b(&in, &out, is_text, f); if (is_text && out.s[out.l-1] != '\n' && restp && restsizep) { *restp = ptr; *restsizep = size; sz = 0; } else { sz = fwrite_td(out.s, sizeof *out.s, out.l, f, dflags, prefix, prefixlen); } free(out.s); break; case CONV_TOB64: sz = mime_write_tob64(&in, f, 0); break; case CONV_FROMHDR: mime_fromhdr(&in, &out, TD_ISPR|TD_ICONV); sz = fwrite_td(out.s, sizeof *out.s, out.l, f, dflags&TD_DELCTRL, prefix, prefixlen); free(out.s); break; case CONV_TOHDR: sz = mime_write_tohdr(&in, f); break; case CONV_TOHDR_A: sz = mime_write_tohdr_a(&in, f); break; default: sz = fwrite_td(in.s, sizeof *in.s, in.l, f, dflags, prefix, prefixlen); } return sz; } heirloom-mailx-12.5/nail.rc000066400000000000000000000035671155563371200156630ustar00rootroot00000000000000# This is the configuration file for Heirloom mailx (formerly # known under the name "nail". # See mailx(1) for further options. # This file is not overwritten when 'make install' is run in # the mailx build process again. # Sccsid @(#)nail.rc 2.11 (gritter) 8/2/08 # Do not forward to mbox by default since this is likely to be # irritating for most users today. set hold # Append rather than prepend when writing to mbox automatically. # This has no effect unless 'hold' is unset again. set append # Ask for a message subject. set ask # Assume a CRT-like terminal and invoke a pager. set crt # Messages may be terminated by a dot. set dot # Do not remove empty mail folders in the spool directory. # This may be relevant for privacy since other users could # otherwise create them with different permissions. set keep # Do not remove empty private mail folders. set emptybox # Quote the original message in replies by "> " as usual on the Internet. set indentprefix="> " # Automatically quote the text of the message that is responded to. set quote # Outgoing messages are sent in ISO-8859-1 if all their characters are # representable in it, otherwise in UTF-8. set sendcharsets=iso-8859-1,utf-8 # Display sender's real names in header summaries. set showname # Display the recipients of messages sent by the user himself in # header summaries. set showto # Automatically check for new messages at each prompt, but avoid polling # of IMAP servers or maildir folders. set newmail=nopoll # If threaded mode is activated, automatically collapse thread. set autocollapse # Mark messages that have been answered. set markanswered # Hide some header fields which are uninteresting for most human readers. ignore received in-reply-to message-id references ignore mime-version content-transfer-encoding # Only include selected header fields when forwarding messages. fwdretain subject date from to heirloom-mailx-12.5/names.c000066400000000000000000000423071155563371200156540ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)names.c 2.22 (gritter) 3/4/06"; #endif #endif /* not lint */ /* * Mail -- a mail program * * Handle name lists. */ #include "rcv.h" #include "extern.h" #include #include #include #include static struct name *tailof(struct name *name); static struct name *extract1(char *line, enum gfield ntype, char *separators, int copypfx); static char *yankword(char *ap, char *wbuf, char *separators, int copypfx); static int same_name(char *n1, char *n2); static struct name *gexpand(struct name *nlist, struct grouphead *gh, int metoo, int ntype); static struct name *put(struct name *list, struct name *node); static struct name *delname(struct name *np, char *name); /* * Allocate a single element of a name list, * initialize its name field to the passed * name and return it. */ struct name * nalloc(char *str, enum gfield ntype) { struct name *np; struct str in, out; /*LINTED*/ np = (struct name *)salloc(sizeof *np); np->n_flink = NULL; np->n_blink = NULL; np->n_type = ntype; if (ntype & GFULL) { np->n_name = savestr(skin(str)); if (strcmp(np->n_name, str)) { in.s = str; in.l = strlen(str); mime_fromhdr(&in, &out, TD_ISPR|TD_ICONV); np->n_fullname = savestr(out.s); free(out.s); } else np->n_fullname = np->n_name; } else if (ntype & GSKIN) np->n_fullname = np->n_name = savestr(skin(str)); else np->n_fullname = np->n_name = savestr(str); return(np); } /* * Find the tail of a list and return it. */ static struct name * tailof(struct name *name) { struct name *np; np = name; if (np == NULL) return(NULL); while (np->n_flink != NULL) np = np->n_flink; return(np); } /* * Extract a list of names from a line, * and make a list of names from it. * Return the list or NULL if none found. */ struct name * extract(char *line, enum gfield ntype) { return extract1(line, ntype, " \t,(", 0); } struct name * sextract(char *line, enum gfield ntype) { if (line && strpbrk(line, ",\"\\(<")) return extract1(line, ntype, ",", 1); else return extract(line, ntype); } static struct name * extract1(char *line, enum gfield ntype, char *separators, int copypfx) { char *cp, *nbuf; struct name *top, *np, *t; if (line == NULL || *line == '\0') return NULL; top = NULL; np = NULL; cp = line; nbuf = ac_alloc(strlen(line) + 1); while ((cp = yankword(cp, nbuf, separators, copypfx)) != NULL) { t = nalloc(nbuf, ntype); if (top == NULL) top = t; else np->n_flink = t; t->n_blink = np; np = t; } ac_free(nbuf); return top; } /* * Turn a list of names into a string of the same names. */ char * detract(struct name *np, enum gfield ntype) { int s; char *cp, *top; struct name *p; int comma; comma = ntype & GCOMMA; if (np == NULL) return(NULL); ntype &= ~GCOMMA; s = 0; if ((debug || value("debug")) && comma) fprintf(stderr, catgets(catd, CATSET, 145, "detract asked to insert commas\n")); for (p = np; p != NULL; p = p->n_flink) { if (ntype && (p->n_type & GMASK) != ntype) continue; s += strlen(p->n_fullname) + 1; if (comma) s++; } if (s == 0) return(NULL); s += 2; top = salloc(s); cp = top; for (p = np; p != NULL; p = p->n_flink) { if (ntype && (p->n_type & GMASK) != ntype) continue; cp = sstpcpy(cp, p->n_fullname); if (comma && p->n_flink != NULL) *cp++ = ','; *cp++ = ' '; } *--cp = 0; if (comma && *--cp == ',') *cp = 0; return(top); } /* * Grab a single word (liberal word) * Throw away things between ()'s, and take anything between <>. */ static char * yankword(char *ap, char *wbuf, char *separators, int copypfx) { char *cp, *pp, *wp; cp = ap; wp = wbuf; while (blankchar(*cp & 0377) || *cp == ',') cp++; pp = cp; if ((cp = nexttoken(cp)) == NULL) return NULL; if (copypfx) while (pp < cp) *wp++ = *pp++; if (*cp == '<') while (*cp && (*wp++ = *cp++) != '>'); else { int incomm = 0; while (*cp && (incomm || !strchr(separators, *cp))) { if (*cp == '\"') { if (cp == ap || *(cp - 1) != '\\') { if (incomm) incomm--; else incomm++; *wp++ = '\"'; } else if (cp != ap) { *(wp - 1) = '\"'; } cp++; continue; } *wp++ = *cp++; } } *wp = '\0'; return cp; } /* * For each recipient in the passed name list with a / * in the name, append the message to the end of the named file * and remove him from the recipient list. * * Recipients whose name begins with | are piped through the given * program and removed. */ /*ARGSUSED 3*/ struct name * outof(struct name *names, FILE *fo, struct header *hp) { int c, lastc; struct name *np, *top; time_t now; char *date, *fname; FILE *fout, *fin; int ispipe; top = names; np = names; time(&now); date = ctime(&now); while (np != NULL) { if (!is_fileaddr(np->n_name) && np->n_name[0] != '|') { np = np->n_flink; continue; } ispipe = np->n_name[0] == '|'; if (ispipe) fname = np->n_name+1; else fname = expand(np->n_name); /* * See if we have copied the complete message out yet. * If not, do so. */ if (image < 0) { char *tempEdit; if ((fout = Ftemp(&tempEdit, "Re", "w", 0600, 1)) == NULL) { perror(catgets(catd, CATSET, 146, "temporary edit file")); senderr++; goto cant; } image = open(tempEdit, O_RDWR); unlink(tempEdit); Ftfree(&tempEdit); if (image < 0) { perror(catgets(catd, CATSET, 147, "temporary edit file")); senderr++; Fclose(fout); goto cant; } fcntl(image, F_SETFD, FD_CLOEXEC); fprintf(fout, "From %s %s", myname, date); c = EOF; while (lastc = c, (c = getc(fo)) != EOF) putc(c, fout); rewind(fo); if (lastc != '\n') putc('\n', fout); putc('\n', fout); fflush(fout); if (ferror(fout)) perror(catgets(catd, CATSET, 148, "temporary edit file")); Fclose(fout); } /* * Now either copy "image" to the desired file * or give it as the standard input to the desired * program as appropriate. */ if (ispipe) { int pid; char *shell; sigset_t nset; /* * XXX * We can't really reuse the same image file, * because multiple piped recipients will * share the same lseek location and trample * on one another. */ if ((shell = value("SHELL")) == NULL) shell = SHELL; sigemptyset(&nset); sigaddset(&nset, SIGHUP); sigaddset(&nset, SIGINT); sigaddset(&nset, SIGQUIT); pid = start_command(shell, &nset, image, -1, "-c", fname, NULL); if (pid < 0) { senderr++; goto cant; } free_child(pid); } else { int f; if ((fout = Zopen(fname, "a", NULL)) == NULL) { perror(fname); senderr++; goto cant; } if ((f = dup(image)) < 0) { perror("dup"); fin = NULL; } else fin = Fdopen(f, "r"); if (fin == NULL) { fprintf(stderr, catgets(catd, CATSET, 149, "Can't reopen image\n")); Fclose(fout); senderr++; goto cant; } rewind(fin); while ((c = getc(fin)) != EOF) putc(c, fout); if (ferror(fout)) senderr++, perror(fname); Fclose(fout); Fclose(fin); } cant: /* * In days of old we removed the entry from the * the list; now for sake of header expansion * we leave it in and mark it as deleted. */ np->n_type |= GDEL; np = np->n_flink; } if (image >= 0) { close(image); image = -1; } return(top); } /* * Determine if the passed address is a local "send to file" address. * If any of the network metacharacters precedes any slashes, it can't * be a filename. We cheat with .'s to allow path names like ./... */ int is_fileaddr(char *name) { char *cp; if (strchr(name, '@') != NULL) return 0; if (*name == '+') return 1; for (cp = name; *cp; cp++) { if (*cp == '!' || *cp == '%') return 0; if (*cp == '/') return 1; } return 0; } static int same_name(char *n1, char *n2) { int c1, c2; if (value("allnet") != NULL) { do { c1 = (*n1++ & 0377); c2 = (*n2++ & 0377); c1 = lowerconv(c1); c2 = lowerconv(c2); if (c1 != c2) return 0; } while (c1 != '\0' && c2 != '\0' && c1 != '@' && c2 != '@'); return 1; } else return asccasecmp(n1, n2) == 0; } /* * Map all of the aliased users in the invoker's mailrc * file and insert them into the list. * Changed after all these months of service to recursively * expand names (2/14/80). */ struct name * usermap(struct name *names) { struct name *new, *np, *cp; struct grouphead *gh; int metoo; new = NULL; np = names; metoo = (value("metoo") != NULL); while (np != NULL) { if (np->n_name[0] == '\\') { cp = np->n_flink; new = put(new, np); np = cp; continue; } gh = findgroup(np->n_name); cp = np->n_flink; if (gh != NULL) new = gexpand(new, gh, metoo, np->n_type); else new = put(new, np); np = cp; } return(new); } /* * Recursively expand a group name. We limit the expansion to some * fixed level to keep things from going haywire. * Direct recursion is not expanded for convenience. */ static struct name * gexpand(struct name *nlist, struct grouphead *gh, int metoo, int ntype) { struct group *gp; struct grouphead *ngh; struct name *np; static int depth; char *cp; if (depth > MAXEXP) { printf(catgets(catd, CATSET, 150, "Expanding alias to depth larger than %d\n"), MAXEXP); return(nlist); } depth++; for (gp = gh->g_list; gp != NULL; gp = gp->ge_link) { cp = gp->ge_name; if (*cp == '\\') goto quote; if (strcmp(cp, gh->g_name) == 0) goto quote; if ((ngh = findgroup(cp)) != NULL) { nlist = gexpand(nlist, ngh, metoo, ntype); continue; } quote: np = nalloc(cp, ntype|GFULL); /* * At this point should allow to expand * to self if only person in group */ if (gp == gh->g_list && gp->ge_link == NULL) goto skip; if (!metoo && same_name(cp, myname)) np->n_type |= GDEL; skip: nlist = put(nlist, np); } depth--; return(nlist); } /* * Concatenate the two passed name lists, return the result. */ struct name * cat(struct name *n1, struct name *n2) { struct name *tail; if (n1 == NULL) return(n2); if (n2 == NULL) return(n1); tail = tailof(n1); tail->n_flink = n2; n2->n_blink = tail; return(n1); } /* * Unpack the name list onto a vector of strings. * Return an error if the name list won't fit. */ char ** unpack(struct name *np) { char **ap, **top; struct name *n; int t, extra, metoo, verbose; n = np; if ((t = count(n)) == 0) panic(catgets(catd, CATSET, 151, "No names to unpack")); /* * Compute the number of extra arguments we will need. * We need at least two extra -- one for "mail" and one for * the terminating 0 pointer. Additional spots may be needed * to pass along -f to the host mailer. */ extra = 2; extra++; metoo = value("metoo") != NULL; if (metoo) extra++; verbose = value("verbose") != NULL; if (verbose) extra++; /*LINTED*/ top = (char **)salloc((t + extra) * sizeof *top); ap = top; *ap++ = "send-mail"; *ap++ = "-i"; if (metoo) *ap++ = "-m"; if (verbose) *ap++ = "-v"; for (; n != NULL; n = n->n_flink) if ((n->n_type & GDEL) == 0) *ap++ = n->n_name; *ap = NULL; return(top); } /* * Remove all of the duplicates from the passed name list by * insertion sorting them, then checking for dups. * Return the head of the new list. */ struct name * elide(struct name *names) { struct name *np, *t, *new; struct name *x; if (names == NULL) return(NULL); new = names; np = names; np = np->n_flink; if (np != NULL) np->n_blink = NULL; new->n_flink = NULL; while (np != NULL) { t = new; while (asccasecmp(t->n_name, np->n_name) < 0) { if (t->n_flink == NULL) break; t = t->n_flink; } /* * If we ran out of t's, put the new entry after * the current value of t. */ if (asccasecmp(t->n_name, np->n_name) < 0) { t->n_flink = np; np->n_blink = t; t = np; np = np->n_flink; t->n_flink = NULL; continue; } /* * Otherwise, put the new entry in front of the * current t. If at the front of the list, * the new guy becomes the new head of the list. */ if (t == new) { t = np; np = np->n_flink; t->n_flink = new; new->n_blink = t; t->n_blink = NULL; new = t; continue; } /* * The normal case -- we are inserting into the * middle of the list. */ x = np; np = np->n_flink; x->n_flink = t; x->n_blink = t->n_blink; t->n_blink->n_flink = x; t->n_blink = x; } /* * Now the list headed up by new is sorted. * Go through it and remove duplicates. */ np = new; while (np != NULL) { t = np; while (t->n_flink != NULL && asccasecmp(np->n_name, t->n_flink->n_name) == 0) t = t->n_flink; if (t == np || t == NULL) { np = np->n_flink; continue; } /* * Now t points to the last entry with the same name * as np. Make np point beyond t. */ np->n_flink = t->n_flink; if (t->n_flink != NULL) t->n_flink->n_blink = np; np = np->n_flink; } return(new); } /* * Put another node onto a list of names and return * the list. */ static struct name * put(struct name *list, struct name *node) { node->n_flink = list; node->n_blink = NULL; if (list != NULL) list->n_blink = node; return(node); } /* * Determine the number of undeleted elements in * a name list and return it. */ int count(struct name *np) { int c; for (c = 0; np != NULL; np = np->n_flink) if ((np->n_type & GDEL) == 0) c++; return c; } /* * Delete the given name from a namelist. */ static struct name * delname(struct name *np, char *name) { struct name *p; for (p = np; p != NULL; p = p->n_flink) if (same_name(p->n_name, name)) { if (p->n_blink == NULL) { if (p->n_flink != NULL) p->n_flink->n_blink = NULL; np = p->n_flink; continue; } if (p->n_flink == NULL) { if (p->n_blink != NULL) p->n_blink->n_flink = NULL; continue; } p->n_blink->n_flink = p->n_flink; p->n_flink->n_blink = p->n_blink; } return np; } /* * Pretty print a name list * Uncomment it if you need it. */ /* void prettyprint(struct name *name) { struct name *np; np = name; while (np != NULL) { fprintf(stderr, "%s(%d) ", np->n_name, np->n_type); np = np->n_flink; } fprintf(stderr, "\n"); } */ struct name * delete_alternates(struct name *np) { struct name *xp; char **ap; np = delname(np, myname); if (altnames) for (ap = altnames; *ap; ap++) np = delname(np, *ap); if ((xp = sextract(value("from"), GEXTRA|GSKIN)) != NULL) while (xp) { np = delname(np, xp->n_name); xp = xp->n_flink; } if ((xp = sextract(value("replyto"), GEXTRA|GSKIN)) != NULL) while (xp) { np = delname(np, xp->n_name); xp = xp->n_flink; } if ((xp = sextract(value("sender"), GEXTRA|GSKIN)) != NULL) while (xp) { np = delname(np, xp->n_name); xp = xp->n_flink; } return np; } int is_myname(char *name) { struct name *xp; char **ap; if (same_name(myname, name)) return 1; if (altnames) for (ap = altnames; *ap; ap++) if (same_name(*ap, name)) return 1; if ((xp = sextract(value("from"), GEXTRA|GSKIN)) != NULL) while (xp) { if (same_name(xp->n_name, name)) return 1; xp = xp->n_flink; } if ((xp = sextract(value("replyto"), GEXTRA|GSKIN)) != NULL) while (xp) { if (same_name(xp->n_name, name)) return 1; xp = xp->n_flink; } if ((xp = sextract(value("sender"), GEXTRA|GSKIN)) != NULL) while (xp) { if (same_name(xp->n_name, name)) return 1; xp = xp->n_flink; } return 0; } heirloom-mailx-12.5/nss.c000066400000000000000000000757451155563371200153700ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Changes Copyright (c) 2004 * Gunnar Ritter. All rights reserved. */ /* * Parts of this file are derived from the Mozilla NSS 3.9.2 source, * mozilla/security/nss/cmd/smimetools/cmsutil.c. Therefore: * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License * at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is the Netscape security libraries. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1994-2000 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)nss.c 1.48 (gritter) 8/4/07"; #endif #endif /* not lint */ #include "config.h" #ifdef USE_NSS #include "rcv.h" #include #include #include static int verbose; static int reset_tio; static struct termios otio; static sigjmp_buf nssjmp; #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_XCONST_H #include #endif #ifdef HAVE_GENNAME_H #include #endif #include #include "extern.h" #ifndef HAVE_CERTAltNameEncodedContext /* * NSS 3.11.5 neither installs genname.h nor provides this * structure otherwise, so define it here. */ typedef struct CERTAltNameEncodedContextStr { SECItem **encodedGenName; } CERTAltNameEncodedContext; #endif /* !HAVE_CERTAltNameEncodedContext */ #include "nsserr.c" static char *password_cb(PK11SlotInfo *slot, PRBool retry, void *arg); static SECStatus bad_cert_cb(void *arg, PRFileDesc *fd); static enum okay nss_check_host(const char *server, struct sock *sp); static const char *bad_cert_str(void); static enum okay nss_init(void); static void nss_select_method(const char *uhp); static CERTCertificate *get_signer_cert(char *addr); static FILE *encode(FILE *ip, FILE **hp, FILE **bp, NSSCMSMessage *msg, void (*cb)(void *, const char *, unsigned long)); static void decoder_cb(void *arg, const char *buf, unsigned long len); static void base64_cb(void *arg, const char *buf, unsigned long len); static int verify1(struct message *m, int n); static struct message *getsig(struct message *m, int n, NSSCMSMessage **msg); static enum okay getdig(struct message *m, int n, SECItem ***digests, PLArenaPool **poolp, SECAlgorithmID **algids); static void nsscatch(int s); static void dumpcert(CERTCertificate *cert, FILE *op); static enum okay getcipher(const char *to, SECOidTag *alg, int *key); static char * password_cb(PK11SlotInfo *slot, PRBool retry, void *arg) { sighandler_type saveint; char *pass = NULL; (void)&saveint; (void)&pass; saveint = safe_signal(SIGINT, SIG_IGN); if (sigsetjmp(nssjmp, 1) == 0) { if (saveint != SIG_IGN) safe_signal(SIGINT, nsscatch); pass = getpassword(&otio, &reset_tio, arg); } safe_signal(SIGINT, saveint); if (pass == NULL) return NULL; return PL_strdup(pass); } static SECStatus bad_cert_cb(void *arg, PRFileDesc *fd) { if (PORT_GetError() == SSL_ERROR_BAD_CERT_DOMAIN) /* * We must not use this result. NSS verifies host names * according to RFC 2818, but we must verify host names * according to RFC 2595. The rules are different: * * - RFC 2818 says that if both a dNSName and a CN are * contained in the peer certificate, only the dNSName * is used. RFC 2595 encourages to use both. * * - RFC 2818 allows the wildcard '*' in any component * of the host name. RFC 2595 allows it only as the * "left-most name component". * * So ignore it and verify separately. */ return SECSuccess; fprintf(stderr, "Error in certificate: %s.\n", bad_cert_str()); return ssl_vrfy_decide() == OKAY ? SECSuccess : SECFailure; } /* * Host name checking according to RFC 2595. */ static enum okay nss_check_host(const char *server, struct sock *sp) { CERTCertificate *cert; char *cn = NULL; enum okay ok = STOP; PRArenaPool *arena; CERTGeneralName *gn; SECItem altname; CERTAltNameEncodedContext ec; int i; const SEC_ASN1Template gntempl[] = { { SEC_ASN1_SEQUENCE_OF, 0, SEC_AnyTemplate } }; if ((cert = SSL_PeerCertificate(sp->s_prfd)) == NULL) { fprintf(stderr, "no certificate from \"%s\"\n", server); return STOP; } arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME, &altname) == SECSuccess && SEC_ASN1DecodeItem(arena, &ec, gntempl, &altname) == SECSuccess && ec.encodedGenName != NULL) { for (i = 0; ec.encodedGenName[i] != NULL; i++) { gn = CERT_DecodeGeneralName(arena, ec.encodedGenName[i], NULL); if (gn->type == certDNSName) { char *dn = ac_alloc(gn->name.other.len + 1); memcpy(dn, gn->name.other.data, gn->name.other.len); dn[gn->name.other.len] = '\0'; if (verbose) fprintf(stderr, "Comparing DNS name: \"%s\"\n", dn); if (rfc2595_hostname_match(server, dn) == OKAY) { ac_free(dn); goto out; } ac_free(dn); } } } if ((cn = CERT_GetCommonName(&cert->subject)) != NULL) { if (verbose) fprintf(stderr, "Comparing common name: \"%s\"\n", cn); ok = rfc2595_hostname_match(server, cn); } if (ok == STOP) fprintf(stderr, "host certificate does not match \"%s\"\n", server); out: if (cn) PORT_Free(cn); PORT_FreeArena(arena, PR_FALSE); CERT_DestroyCertificate(cert); return ok; } static const char * bad_cert_str(void) { int ec; ec = PORT_GetError(); return nss_strerror(ec); } static enum okay nss_init(void) { static int initialized; char *cp; verbose = value("verbose") != NULL; if (initialized == 0) { if ((cp = value("nss-config-dir")) == NULL) { fputs("Missing \"nss-config-dir\" variable.\n", stderr); return STOP; } cp = expand(cp); PR_Init(0, 0, 0); PK11_SetPasswordFunc(password_cb); if (NSS_Init(cp) == SECSuccess) { NSS_SetDomesticPolicy(); initialized = 1; return OKAY; } nss_gen_err("Error initializing NSS"); return STOP; } return OKAY; } static void nss_select_method(const char *uhp) { char *cp; enum { SSL2 = 01, SSL3 = 02, TLS1 = 03 } methods; methods = SSL2|SSL3|TLS1; cp = ssl_method_string(uhp); if (cp != NULL) { if (equal(cp, "ssl2")) methods = SSL2; else if (equal(cp, "ssl3")) methods = SSL3; else if (equal(cp, "tls1")) methods = TLS1; else { fprintf(stderr, catgets(catd, CATSET, 244, "Invalid SSL method \"%s\"\n"), cp); } } if (value("ssl-v2-allow") == NULL) methods &= ~SSL2; SSL_OptionSetDefault(SSL_ENABLE_SSL2, methods&SSL2 ? PR_TRUE:PR_FALSE); SSL_OptionSetDefault(SSL_ENABLE_SSL3, methods&SSL3 ? PR_TRUE:PR_FALSE); SSL_OptionSetDefault(SSL_ENABLE_TLS, methods&TLS1 ? PR_TRUE:PR_FALSE); } enum okay ssl_open(const char *server, struct sock *sp, const char *uhp) { PRFileDesc *fdp, *fdc; if (nss_init() == STOP) return STOP; ssl_set_vrfy_level(uhp); nss_select_method(uhp); if ((fdp = PR_ImportTCPSocket(sp->s_fd)) == NULL) { nss_gen_err("Error importing OS file descriptor"); return STOP; } if ((fdc = SSL_ImportFD(NULL, fdp)) == NULL) { nss_gen_err("Error importing NSPR file descriptor"); PR_Close(fdp); return STOP; } SSL_SetURL(fdc, server); SSL_SetPKCS11PinArg(fdc, NULL); SSL_BadCertHook(fdc, bad_cert_cb, NULL); if (SSL_ResetHandshake(fdc, PR_FALSE) != SECSuccess) { nss_gen_err("Cannot reset NSS handshake"); PR_Close(fdc); return STOP; } if (SSL_ForceHandshake(fdc) != 0) { nss_gen_err("SSL/TLS handshake failed"); PR_Close(fdc); return STOP; } sp->s_prfd = fdc; if (nss_check_host(server, sp) != OKAY && ssl_vrfy_decide() != OKAY) { PR_Close(fdc); sp->s_prfd = NULL; return STOP; } sp->s_use_ssl = 1; if (verbose) { char *cipher, *issuer, *subject; int keysize, secretkeysize; if (SSL_SecurityStatus(fdc, NULL, &cipher, &keysize, &secretkeysize, &issuer, &subject) == SECSuccess) { fprintf(stderr, "SSL parameters: cipher=%s, " "keysize=%d, secretkeysize=%d,\n" "issuer=%s\n" "subject=%s\n", cipher, keysize, secretkeysize, issuer, subject); PR_Free(cipher); PR_Free(issuer); PR_Free(subject); } else nss_gen_err("Could not read status information"); } return OKAY; } void nss_gen_err(const char *fmt, ...) { va_list ap; char *text; int len; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); if ((len = PR_GetErrorTextLength()) > 0) { text = ac_alloc(len); if (PR_GetErrorText(text) > 0) fprintf(stderr, ": %s\n", text); ac_free(text); } else fprintf(stderr, ": %s.\n", nss_strerror(PR_GetError())); } FILE * smime_sign(FILE *ip, struct header *headp) { NSSCMSMessage *msg; NSSCMSContentInfo *content; NSSCMSSignedData *data; NSSCMSSignerInfo *info; CERTCertificate *cert; CERTCertDBHandle *handle; FILE *hp, *bp, *sp; char *addr; if (nss_init() != OKAY) return NULL; if ((addr = myorigin(headp)) == NULL) { fprintf(stderr, "No \"from\" address for signing specified\n"); return NULL; } if ((cert = get_signer_cert(addr)) == NULL) return NULL; handle = CERT_GetDefaultCertDB(); if ((msg = NSS_CMSMessage_Create(NULL)) == NULL) { fprintf(stderr, "Cannot create CMS message.\n"); return NULL; } if ((data = NSS_CMSSignedData_Create(msg)) == NULL) { fprintf(stderr, "Cannot create CMS signed data.\n"); return NULL; } content = NSS_CMSMessage_GetContentInfo(msg); if (NSS_CMSContentInfo_SetContent_SignedData(msg, content, data) != SECSuccess) { fprintf(stderr, "Cannot attach CMS signed data.\n"); return NULL; } content = NSS_CMSSignedData_GetContentInfo(data); if (NSS_CMSContentInfo_SetContent_Data(msg, content, NULL, PR_TRUE) != SECSuccess) { fprintf(stderr, "Cannot attach CMS data.\n"); return NULL; } if ((info = NSS_CMSSignerInfo_Create(msg, cert, SEC_OID_SHA1)) == 0) { fprintf(stderr, "Cannot create signed information.\n"); return NULL; } if (NSS_CMSSignerInfo_IncludeCerts(info, NSSCMSCM_CertOnly, certUsageEmailSigner) != SECSuccess) { fprintf(stderr, "Cannot include certificate.\n"); return NULL; } if (NSS_CMSSignerInfo_AddSigningTime(info, PR_Now()) != SECSuccess) { fprintf(stderr, "Cannot add signing time.\n"); return NULL; } if (NSS_CMSSignerInfo_AddSMIMECaps(info) != SECSuccess) { fprintf(stderr, "Cannot add S/MIME capabilities.\n"); return NULL; } NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(info, cert, handle); NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(info, cert, handle); if (NSS_CMSSignedData_AddCertificate(data, cert) != SECSuccess) { fprintf(stderr, "Cannot add encryption certificate.\n"); return NULL; } if (NSS_CMSSignedData_AddSignerInfo(data, info) != SECSuccess) { fprintf(stderr, "Cannot add signer information.\n"); return NULL; } CERT_DestroyCertificate(cert); if ((sp = encode(ip, &hp, &bp, msg, base64_cb)) == NULL) { NSS_CMSMessage_Destroy(msg); return NULL; } NSS_CMSMessage_Destroy(msg); return smime_sign_assemble(hp, bp, sp); } int cverify(void *vp) { int *msgvec = vp, *ip; int ec = 0; if (nss_init() != OKAY) return 1; ssl_vrfy_level = VRFY_STRICT; for (ip = msgvec; *ip; ip++) { setdot(&message[*ip-1]); ec |= verify1(&message[*ip-1], *ip); } return ec; } FILE * smime_encrypt(FILE *ip, const char *ignored, const char *to) { NSSCMSMessage *msg; NSSCMSContentInfo *content; NSSCMSEnvelopedData *data; NSSCMSRecipientInfo *info; CERTCertificate *cert[2]; CERTCertDBHandle *handle; SECOidTag tag; FILE *hp, *pp, *yp; int keysize; char *nickname, *vn; int vs; if (nss_init() != OKAY) return NULL; handle = CERT_GetDefaultCertDB(); vn = ac_alloc(vs = strlen(to) + 30); snprintf(vn, vs, "smime-nickname-%s", to); nickname = value(vn); ac_free(vn); if ((cert[0] = CERT_FindCertByNicknameOrEmailAddr(handle, nickname ? nickname : (char *)to)) == NULL) { if (nickname) fprintf(stderr, "Cannot find certificate \"%s\".\n", nickname); else fprintf(stderr, "Cannot find certificate for <%s>.\n", to); return NULL; } cert[1] = NULL; if (getcipher(to, &tag, &keysize) != OKAY) return NULL; if ((msg = NSS_CMSMessage_Create(NULL)) == NULL) { fprintf(stderr, "Cannot create CMS message.\n"); return NULL; } if ((data = NSS_CMSEnvelopedData_Create(msg, tag, keysize)) == NULL) { fprintf(stderr, "Cannot create enveloped data.\n"); return NULL; } content = NSS_CMSMessage_GetContentInfo(msg); if (NSS_CMSContentInfo_SetContent_EnvelopedData(msg, content, data) != SECSuccess) { fprintf(stderr, "Cannot attach enveloped data.\n"); return NULL; } content = NSS_CMSEnvelopedData_GetContentInfo(data); if (NSS_CMSContentInfo_SetContent_Data(msg, content, NULL, PR_FALSE) != SECSuccess) { fprintf(stderr, "Cannot attach CMS data.\n"); return NULL; } if ((info = NSS_CMSRecipientInfo_Create(msg, cert[0])) == NULL) { fprintf(stderr, "Cannot create CMS recipient information.\n"); return NULL; } if (NSS_CMSEnvelopedData_AddRecipient(data, info) != SECSuccess) { fprintf(stderr, "Cannot add CMS recipient information.\n"); return NULL; } CERT_DestroyCertificate(cert[0]); if ((yp = encode(ip, &hp, &pp, msg, base64_cb)) == NULL) return NULL; NSS_CMSMessage_Destroy(msg); return smime_encrypt_assemble(hp, yp); } struct message * smime_decrypt(struct message *m, const char *to, const char *cc, int signcall) { NSSCMSDecoderContext *ctx; NSSCMSMessage *msg; FILE *op, *hp, *bp; char *buf = NULL; size_t bufsize = 0, buflen, count; char *cp; struct str in, out; FILE *yp; long size; int i, nlevels; int binary = 0; if ((yp = setinput(&mb, m, NEED_BODY)) == NULL) return NULL; if (nss_init() != OKAY) return NULL; if ((op = Ftemp(&cp, "Rp", "w+", 0600, 1)) == NULL) { perror("tempfile"); return NULL; } rm(cp); Ftfree(&cp); if ((ctx = NSS_CMSDecoder_Start(NULL, decoder_cb, op, password_cb, "Pass phrase:", NULL, NULL)) == NULL) { fprintf(stderr, "Cannot start decoder.\n"); return NULL; } size = m->m_size; if ((smime_split(yp, &hp, &bp, size, 1)) == STOP) return NULL; count = fsize(bp); while (fgetline(&buf, &bufsize, &count, &buflen, bp, 0) != NULL) { if (buf[0] == '\n') break; if ((cp = thisfield(buf, "content-transfer-encoding")) != NULL) if (ascncasecmp(cp, "binary", 7) == 0) binary = 1; } while (fgetline(&buf, &bufsize, &count, &buflen, bp, 0) != NULL) { if (binary) NSS_CMSDecoder_Update(ctx, buf, buflen); else { in.s = buf; in.l = buflen; mime_fromb64_b(&in, &out, 0, bp); NSS_CMSDecoder_Update(ctx, out.s, out.l); free(out.s); } } free(buf); if ((msg = NSS_CMSDecoder_Finish(ctx)) == NULL) { fprintf(stderr, "Failed to decode message.\n"); Fclose(hp); Fclose(bp); return NULL; } nlevels = NSS_CMSMessage_ContentLevelCount(msg); for (i = 0; i < nlevels; i++) { NSSCMSContentInfo *content; SECOidTag tag; content = NSS_CMSMessage_ContentLevel(msg, i); tag = NSS_CMSContentInfo_GetContentTypeTag(content); if (tag == SEC_OID_PKCS7_DATA) { const char *fld = "X-Encryption-Cipher"; SECOidTag alg; int keysize; alg = NSS_CMSContentInfo_GetContentEncAlgTag(content); keysize = NSS_CMSContentInfo_GetBulkKeySize(content); fseek(hp, 0L, SEEK_END); switch (alg) { case 0: if (signcall) { NSS_CMSMessage_Destroy(msg); Fclose(hp); Fclose(bp); setinput(&mb, m, NEED_BODY); return (struct message *)-1; } fprintf(hp, "%s: none\n", fld); break; case SEC_OID_RC2_CBC: fprintf(hp, "%s: RC2, %d bits\n", fld, keysize); break; case SEC_OID_DES_CBC: fprintf(hp, "%s: DES, 56 bits\n", fld); break; case SEC_OID_DES_EDE3_CBC: fprintf(hp, "%s: 3DES, 112/168 bits\n", fld); break; case SEC_OID_FORTEZZA_SKIPJACK: fprintf(hp, "%s: Fortezza\n", fld); break; default: fprintf(hp, "%s: unknown type %lu\n", fld, (unsigned long)alg); } fflush(hp); rewind(hp); } } NSS_CMSMessage_Destroy(msg); fflush(op); rewind(op); Fclose(bp); return smime_decrypt_assemble(m, hp, op); } static CERTCertificate * get_signer_cert(char *addr) { CERTCertDBHandle *handle; CERTCertList *list; CERTCertListNode *node; CERTCertificate *cert = NULL; const char *cp; char *nick; char *vn; int vs, found = 0; addr = skin(addr); vn = ac_alloc(vs = strlen(addr) + 30); snprintf(vn, vs, "smime-sign-nickname-%s", addr); if ((nick = value(vn)) == NULL) nick = value("smime-sign-nickname"); ac_free(vn); handle = CERT_GetDefaultCertDB(); if (nick) { cert = CERT_FindCertByNickname(handle, nick); if (cert == NULL) fprintf(stderr, "No certificate \"%s\" found.\n", nick); return cert; } if ((list = CERT_FindUserCertsByUsage(handle, certUsageEmailSigner, PR_TRUE, PR_TRUE, NULL)) == NULL) { fprintf(stderr, "Cannot find any certificates for signing.\n"); return NULL; } for (node = CERT_LIST_HEAD(list); !CERT_LIST_END(node, list); node = CERT_LIST_NEXT(node)) { if ((cp = CERT_GetCertEmailAddress(&node->cert->subject)) != NULL && asccasecmp(cp, addr) == 0) { cert = node->cert; found++; } } if (cert == NULL) { for (node = CERT_LIST_HEAD(list); !CERT_LIST_END(node, list) && cert == NULL; node = CERT_LIST_NEXT(node)) { cp = CERT_GetFirstEmailAddress(node->cert); while (cp) { if (asccasecmp(cp, addr) == 0) { cert = node->cert; found++; } cp = CERT_GetNextEmailAddress(node->cert, cp); } } } if (found > 1) { fprintf(stderr, "More than one signing certificate found for <%s>.\n" "Use the smime-sign-nickname variable.\n", addr); return NULL; } if (cert == NULL) fprintf(stderr, "Cannot find a signing certificate for <%s>.\n", addr); return cert; } static FILE * encode(FILE *ip, FILE **hp, FILE **bp, NSSCMSMessage *msg, void (*cb)(void *, const char *, unsigned long)) { NSSCMSEncoderContext *ctx; char *buf = NULL, *cp; size_t bufsize = 0, buflen, count; FILE *op; if (smime_split(ip, hp, bp, -1, 0) == STOP) return NULL; if ((op = Ftemp(&cp, "Ry", "w+", 0600, 1)) == NULL) { perror("tempfile"); return NULL; } rm(cp); Ftfree(&cp); if ((ctx = NSS_CMSEncoder_Start(msg, cb, op, NULL, NULL, password_cb, "Pass phrase:", NULL, NULL, NULL, NULL)) == NULL) { fprintf(stderr, "Cannot create encoder context.\n"); Fclose(op); return NULL; } count = fsize(*bp); while (fgetline(&buf, &bufsize, &count, &buflen, *bp, 0) != NULL) { buf[buflen-1] = '\r'; buf[buflen] = '\n'; if (NSS_CMSEncoder_Update(ctx, buf, buflen+1) != 0) { fprintf(stderr, "Failed to add data to encoder.\n"); Fclose(op); return NULL; } } free(buf); if (NSS_CMSEncoder_Finish(ctx) != 0) { fprintf(stderr, "Failed to encode data.\n"); Fclose(op); return NULL; } rewind(*bp); cb(op, (void *)-1, 0); fflush(op); if (ferror(op)) { perror("tempfile"); Fclose(op); return NULL; } rewind(op); return op; } static void decoder_cb(void *arg, const char *buf, unsigned long len) { if (arg && buf) fwrite(buf, 1, len, arg); } static void base64_cb(void *arg, const char *buf, unsigned long len) { static char back[972]; static int fill; unsigned long pos; if (arg && buf && buf != (void *)-1) { pos = 0; while (len - pos >= sizeof back - fill) { memcpy(&back[fill], &buf[pos], sizeof back - fill); mime_write(back, sizeof back, arg, CONV_TOB64, TD_NONE, NULL, 0, NULL, NULL); pos += sizeof back - fill; fill = 0; } memcpy(&back[fill], &buf[pos], len - pos); fill += len - pos; } else if (buf == (void *)-1) { mime_write(back, fill, arg, CONV_TOB64, TD_NONE, NULL, 0, NULL, NULL); fill = 0; } } static int verify1(struct message *m, int n) { SECItem **digests; NSSCMSMessage *msg; PLArenaPool *poolp; SECAlgorithmID **algids; CERTCertDBHandle *handle; int nlevels, i; int status = 0; int foundsender = 0; char *sender; if ((m = getsig(m, n, &msg)) == NULL) return 1; sender = getsender(m); handle = CERT_GetDefaultCertDB(); nlevels = NSS_CMSMessage_ContentLevelCount(msg); for (i = 0; i < nlevels; i++) { NSSCMSContentInfo *content; SECOidTag tag; content = NSS_CMSMessage_ContentLevel(msg, i); tag = NSS_CMSContentInfo_GetContentTypeTag(content); if (tag == SEC_OID_PKCS7_SIGNED_DATA) { NSSCMSSignedData *data; int nsigners, j; if ((data = NSS_CMSContentInfo_GetContent(content)) == NULL) { fprintf(stderr, "Signed data missing for " "message %d.\n", n); status = -1; break; } if (!NSS_CMSSignedData_HasDigests(data)) { algids = NSS_CMSSignedData_GetDigestAlgs(data); if (getdig(m, n, &digests, &poolp, algids) != OKAY) { status = -1; break; } if (NSS_CMSSignedData_SetDigests(data, algids, digests) != SECSuccess) { fprintf(stderr, "Cannot set digests " "for message %d.\n", n); status = -1; break; } PORT_FreeArena(poolp, PR_FALSE); } if (NSS_CMSSignedData_ImportCerts(data, handle, certUsageEmailSigner, PR_FALSE) != SECSuccess) { fprintf(stderr, "Cannot temporarily import " "certificates for " "message %d.\n", n); status = -1; break; } nsigners = NSS_CMSSignedData_SignerInfoCount(data); if (nsigners == 0) { fprintf(stderr, "Message %d has no signers.\n", n); status = -1; break; } if (!NSS_CMSSignedData_HasDigests(data)) { fprintf(stderr, "Message %d has no digests.\n", n); status = -1; break; } for (j = 0; j < nsigners; j++) { const char *svs; NSSCMSSignerInfo *info; NSSCMSVerificationStatus vs; SECStatus bad; CERTCertificate *cert; const char *addr; int passed = 0; info = NSS_CMSSignedData_GetSignerInfo(data, j); cert = NSS_CMSSignerInfo_GetSigningCertificate (info, handle); bad = NSS_CMSSignedData_VerifySignerInfo(data, j, handle, certUsageEmailSigner); vs = NSS_CMSSignerInfo_GetVerificationStatus (info); svs = NSS_CMSUtil_VerificationStatusToString (vs); addr = CERT_GetCertEmailAddress(&cert->subject); if (sender != NULL && addr != NULL && asccasecmp(sender, addr) == 0) foundsender++; else { addr = CERT_GetFirstEmailAddress(cert); while (sender && addr) { if (!asccasecmp(sender, addr)) { foundsender++; break; } addr = CERT_GetNextEmailAddress (cert, addr); } } if (CERT_VerifyCertNow(handle, cert, PR_TRUE, certUsageEmailSigner, NULL) != SECSuccess) fprintf(stderr, "Bad certificate for " "signer <%s> of " "message %d: %s.\n", addr ? addr : "?", n, bad_cert_str()); else passed++; if (bad) fprintf(stderr, "Bad status for " "signer <%s> of " "message %d: %s.\n", addr ? addr : "?", n, svs); else passed++; if (passed < 2) status = -1; else if (status == 0) status = 1; } } } if (foundsender == 0) { if (sender) { fprintf(stderr, "Signers of message " "%d do not include the sender <%s>\n", n, sender); status = -1; } else fprintf(stderr, "Warning: Message %d has no From: " "header field.\n", n); } else if (status == 1) printf("Message %d was verified successfully.\n", n); if (status == 0) fprintf(stderr, "No verification information found in " "message %d.\n", n); NSS_CMSMessage_Destroy(msg); return status != 1; } static struct message * getsig(struct message *m, int n, NSSCMSMessage **msg) { struct message *x; char *ct, *pt, *boundary = NULL, *cte; char *buf = NULL; size_t bufsize = 0, buflen, count, boundlen = -1; int part; FILE *fp; NSSCMSDecoderContext *decctx; struct str in, out; char *to, *cc; int inhdr, binary; int detached = 1; loop: if ((ct = hfield("content-type", m)) == NULL) goto not; if (strncmp(ct, "application/x-pkcs7-mime", 24) == 0 || strncmp(ct, "application/pkcs7-mime", 22) == 0) { to = hfield("to", m); cc = hfield("cc", m); if ((x = smime_decrypt(m, to, cc, 1)) == NULL) return NULL; if (x != (struct message *)-1) { m = x; goto loop; } detached = 0; } else if (strncmp(ct, "multipart/signed", 16) || (pt = mime_getparam("protocol", ct)) == NULL || strcmp(pt, "application/x-pkcs7-signature") && strcmp(pt, "application/pkcs7-signature") || (boundary = mime_getboundary(ct)) == NULL) { not: fprintf(stderr, "Message %d is not an S/MIME signed message.\n", n); return NULL; } else boundlen = strlen(boundary); if ((decctx = NSS_CMSDecoder_Start(NULL, NULL, NULL, password_cb, "Pass phrase:", NULL, NULL)) == NULL) { fprintf(stderr, "Cannot start decoder.\n"); return NULL; } if ((fp = setinput(&mb, m, NEED_BODY)) == NULL) { return NULL; } count = m->m_size; part = 0; inhdr = 1; binary = 0; while (fgetline(&buf, &bufsize, &count, &buflen, fp, 0) != NULL) { if (detached && boundary && buflen >= boundlen + 1 && strncmp(buf, boundary, boundlen) == 0) { if (buf[boundlen] == '\n') { part++; inhdr = 1; binary = 0; if (part >= 3) { fprintf(stderr, "Message %d has too " "many parts.\n", n); free(buf); return NULL; } continue; } if (buf[boundlen] == '-' && buf[boundlen+1] == '-' && buf[boundlen+2] == '\n') break; } else if (buf[0] == '\n') { inhdr = 0; continue; } if ((!detached || part == 2) && inhdr == 0) { if (binary) NSS_CMSDecoder_Update(decctx, buf, buflen); else { in.s = buf; in.l = buflen; mime_fromb64_b(&in, &out, 0, fp); NSS_CMSDecoder_Update(decctx, out.s, out.l); free(out.s); } } if (buflen == 1 && buf[0] == '\n') inhdr = 0; if (inhdr && (cte = thisfield(buf, "content-transfer-encoding")) != NULL && ascncasecmp(cte, "binary", 7) == 0) binary = 1; } free(buf); if ((*msg = NSS_CMSDecoder_Finish(decctx)) == NULL) { fprintf(stderr, "Failed to decode signature for message %d.\n", n); return NULL; } return m; } static enum okay getdig(struct message *m, int n, SECItem ***digests, PLArenaPool **poolp, SECAlgorithmID **algids) { char *ct, *pt, *boundary; char *buf = NULL; size_t bufsize = 0, buflen, count, boundlen; int part; int nl; FILE *fp; NSSCMSDigestContext *digctx; *poolp = PORT_NewArena(1024); if ((ct = hfield("content-type", m)) == NULL || strncmp(ct, "multipart/signed", 16) || (pt = mime_getparam("protocol", ct)) == NULL || strcmp(pt, "application/x-pkcs7-signature") && strcmp(pt, "application/pkcs7-signature") || (boundary = mime_getboundary(ct)) == NULL) { fprintf(stderr, "Message %d is not an S/MIME signed message.\n", n); return STOP; } boundlen = strlen(boundary); if ((digctx = NSS_CMSDigestContext_StartMultiple(algids)) == NULL) { fprintf(stderr, "Cannot start digest computation.\n"); return STOP; } if ((fp = setinput(&mb, m, NEED_BODY)) == NULL) { return STOP; } count = m->m_size; part = 0; nl = 0; while (fgetline(&buf, &bufsize, &count, &buflen, fp, 0) != NULL) { if (buflen >= boundlen + 1 && strncmp(buf, boundary, boundlen) == 0) { if (buf[boundlen] == '\n') { if (++part >= 2) break; continue; } if (buf[boundlen] == '-' && buf[boundlen+1] == '-' && buf[boundlen+2] == '\n') break; } if (part == 1) { if (nl) { NSS_CMSDigestContext_Update(digctx, (unsigned char *)"\r\n", 2); nl = 0; } if (buf[buflen-1] == '\n') { nl = 1; buflen--; } NSS_CMSDigestContext_Update(digctx, (unsigned char *)buf, buflen); continue; } } free(buf); if (NSS_CMSDigestContext_FinishMultiple(digctx, *poolp, digests) != SECSuccess) { fprintf(stderr, "Error creating digest for message %d\n", n); return STOP; } return OKAY; } static void nsscatch(int s) { if (reset_tio) tcsetattr(0, TCSADRAIN, &otio); siglongjmp(nssjmp, s); } enum okay smime_certsave(struct message *m, int n, FILE *op) { NSSCMSMessage *msg; CERTCertDBHandle *handle; int nlevels, i, cnt = 0; enum okay ok = OKAY; if (nss_init() == STOP) return STOP; if ((m = getsig(m, n, &msg)) == NULL) return 1; handle = CERT_GetDefaultCertDB(); nlevels = NSS_CMSMessage_ContentLevelCount(msg); for (i = 0; i < nlevels; i++) { NSSCMSContentInfo *content; SECOidTag tag; content = NSS_CMSMessage_ContentLevel(msg, i); tag = NSS_CMSContentInfo_GetContentTypeTag(content); if (tag == SEC_OID_PKCS7_SIGNED_DATA) { NSSCMSSignedData *data; int nsigners, j; if ((data = NSS_CMSContentInfo_GetContent(content)) == NULL) { fprintf(stderr, "Signed data missing for " "message %d.\n", n); ok = STOP; break; } if (NSS_CMSSignedData_ImportCerts(data, handle, certUsageEmailSigner, PR_FALSE) != SECSuccess) { fprintf(stderr, "Cannot temporarily import " "certificates for " "message %d.\n", n); ok = STOP; break; } nsigners = NSS_CMSSignedData_SignerInfoCount(data); if (nsigners == 0) { fprintf(stderr, "Message %d has no signers.\n", n); ok = STOP; break; } for (j = 0; j < nsigners; j++) { NSSCMSSignerInfo *info; CERTCertificateList *list; CERTCertificate *cert; int k; info = NSS_CMSSignedData_GetSignerInfo(data, j); list = NSS_CMSSignerInfo_GetCertList(info); if (list) { for (k = 0; k < list->len; k++) { cert = (CERTCertificate *) &list->certs[k]; dumpcert(cert, op); cnt++; } } cert = NSS_CMSSignerInfo_GetSigningCertificate (info, handle); if (cert) { dumpcert(cert, op); cnt++; } } } } NSS_CMSMessage_Destroy(msg); if (cnt == 0) { fprintf(stderr, "No certificates found in message %d.\n", n); ok = STOP; } return ok; } static void dumpcert(CERTCertificate *cert, FILE *op) { fprintf(op, "subject=%s\n", cert->subjectName); fprintf(op, "issuer=%s\n", cert->issuerName); fputs("-----BEGIN CERTIFICATE-----\n", op); mime_write(cert->derCert.data, cert->derCert.len, op, CONV_TOB64, TD_NONE, NULL, 0, NULL, NULL); fputs("-----END CERTIFICATE-----\n", op); } static enum okay getcipher(const char *to, SECOidTag *alg, int *key) { char *vn, *cp; int vs; *key = 0; *alg = SEC_OID_DES_EDE3_CBC; vn = ac_alloc(vs = strlen(to) + 30); snprintf(vn, vs, "smime-cipher-%s", to); if ((cp = value(vn)) != NULL) { if (strcmp(cp, "rc2-40") == 0) { *alg = SEC_OID_RC2_CBC; *key = 40; } else if (strcmp(cp, "rc2-64") == 0) { *alg = SEC_OID_RC2_CBC; *key = 64; } else if (strcmp(cp, "rc2-128") == 0) { *alg = SEC_OID_RC2_CBC; *key = 128; } else if (strcmp(cp, "des") == 0) *alg = SEC_OID_DES_CBC; else if (strcmp(cp, "fortezza") == 0) *alg = SEC_OID_FORTEZZA_SKIPJACK; else if (strcmp(cp, "des-ede3") == 0) /*EMPTY*/; else { fprintf(stderr, "Invalid cipher \"%s\".\n", cp); return STOP; } } ac_free(vn); return OKAY; } #endif /* USE_NSS */ heirloom-mailx-12.5/nsserr.c000066400000000000000000000673061155563371200160730ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2005 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Generated from * . * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License * at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is the Netscape security libraries. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1994-2000 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): */ /* "@(#)nsserr.c 1.3 (gritter) 3/4/06" */ #include #include static const char * nss_strerror(int ec) { const char *cp; static char eb[30]; switch (ec) { default: snprintf(eb, sizeof eb, "Unknown error %d", ec); cp = eb; break; case SSL_ERROR_EXPORT_ONLY_SERVER: cp = "Unable to communicate securely. Peer does not support high-grade encryption"; break; case SSL_ERROR_US_ONLY_SERVER: cp = "Unable to communicate securely. Peer requires high-grade encryption which is not supported"; break; case SSL_ERROR_NO_CYPHER_OVERLAP: cp = "Cannot communicate securely with peer: no common encryption algorithm(s)"; break; case SSL_ERROR_NO_CERTIFICATE: cp = "Unable to find the certificate or key necessary for authentication"; break; case SSL_ERROR_BAD_CERTIFICATE: cp = "Unable to communicate securely with peer: peers's certificate was rejected"; break; case SSL_ERROR_BAD_CLIENT: cp = "The server has encountered bad data from the client"; break; case SSL_ERROR_BAD_SERVER: cp = "The client has encountered bad data from the server"; break; case SSL_ERROR_UNSUPPORTED_CERTIFICATE_TYPE: cp = "Unsupported certificate type"; break; case SSL_ERROR_UNSUPPORTED_VERSION: cp = "Peer using unsupported version of security protocol"; break; case SSL_ERROR_WRONG_CERTIFICATE: cp = "Client authentication failed: private key in key database does not correspond to public key in certificate database"; break; case SSL_ERROR_BAD_CERT_DOMAIN: cp = "Unable to communicate securely with peer: requested domain name does not match the server's certificate"; break; case SSL_ERROR_POST_WARNING: cp = "(unused)"; break; case SSL_ERROR_SSL2_DISABLED: cp = "Peer only supports SSL version 2, which is locally disabled"; break; case SSL_ERROR_BAD_MAC_READ: cp = "SSL received a record with an incorrect Message Authentication Code"; break; case SSL_ERROR_BAD_MAC_ALERT: cp = "SSL peer reports incorrect Message Authentication Code"; break; case SSL_ERROR_BAD_CERT_ALERT: cp = "SSL peer cannot verify your certificate"; break; case SSL_ERROR_REVOKED_CERT_ALERT: cp = "SSL peer rejected your certificate as revoked"; break; case SSL_ERROR_EXPIRED_CERT_ALERT: cp = "SSL peer rejected your certificate as expired"; break; case SSL_ERROR_SSL_DISABLED: cp = "Cannot connect: SSL is disabled"; break; case SSL_ERROR_FORTEZZA_PQG: cp = "Cannot connect: SSL peer is in another FORTEZZA domain"; break; case SSL_ERROR_UNKNOWN_CIPHER_SUITE: cp = "An unknown SSL cipher suite has been requested"; break; case SSL_ERROR_NO_CIPHERS_SUPPORTED: cp = "No cipher suites are present and enabled in this program"; break; case SSL_ERROR_BAD_BLOCK_PADDING: cp = "SSL received a record with bad block padding"; break; case SSL_ERROR_RX_RECORD_TOO_LONG: cp = "SSL received a record that exceeded the maximum permissible length"; break; case SSL_ERROR_TX_RECORD_TOO_LONG: cp = "SSL attempted to send a record that exceeded the maximum permissible length"; break; case SSL_ERROR_CLOSE_NOTIFY_ALERT: cp = "SSL peer has closed this connection"; break; case SSL_ERROR_PUB_KEY_SIZE_LIMIT_EXCEEDED: cp = "SSL Server attempted to use domestic-grade public key with export cipher suite"; break; case SSL_ERROR_NO_SERVER_KEY_FOR_ALG: cp = "Server has no key for the attempted key exchange algorithm"; break; case SSL_ERROR_TOKEN_INSERTION_REMOVAL: cp = "PKCS #11 token was inserted or removed while operation was in progress"; break; case SSL_ERROR_TOKEN_SLOT_NOT_FOUND: cp = "No PKCS#11 token could be found to do a required operation"; break; case SSL_ERROR_NO_COMPRESSION_OVERLAP: cp = "Cannot communicate securely with peer: no common compression algorithm(s)"; break; case SSL_ERROR_HANDSHAKE_NOT_COMPLETED: cp = "Cannot initiate another SSL handshake until current handshake is complete"; break; case SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE: cp = "Received incorrect handshakes hash values from peer"; break; case SSL_ERROR_CERT_KEA_MISMATCH: cp = "The certificate provided cannot be used with the selected key exchange algorithm"; break; case SSL_ERROR_NO_TRUSTED_SSL_CLIENT_CA: cp = "No certificate authority is trusted for SSL client authentication"; break; case SSL_ERROR_SESSION_NOT_FOUND: cp = "Client's SSL session ID not found in server's session cache"; break; case SSL_ERROR_RX_MALFORMED_HELLO_REQUEST: cp = "SSL received a malformed Hello Request handshake message"; break; case SSL_ERROR_RX_MALFORMED_CLIENT_HELLO: cp = "SSL received a malformed Client Hello handshake message"; break; case SSL_ERROR_RX_MALFORMED_SERVER_HELLO: cp = "SSL received a malformed Server Hello handshake message"; break; case SSL_ERROR_RX_MALFORMED_CERTIFICATE: cp = "SSL received a malformed Certificate handshake message"; break; case SSL_ERROR_RX_MALFORMED_SERVER_KEY_EXCH: cp = "SSL received a malformed Server Key Exchange handshake message"; break; case SSL_ERROR_RX_MALFORMED_CERT_REQUEST: cp = "SSL received a malformed Certificate Request handshake message"; break; case SSL_ERROR_RX_MALFORMED_HELLO_DONE: cp = "SSL received a malformed Server Hello Done handshake message"; break; case SSL_ERROR_RX_MALFORMED_CERT_VERIFY: cp = "SSL received a malformed Certificate Verify handshake message"; break; case SSL_ERROR_RX_MALFORMED_CLIENT_KEY_EXCH: cp = "SSL received a malformed Client Key Exchange handshake message"; break; case SSL_ERROR_RX_MALFORMED_FINISHED: cp = "SSL received a malformed Finished handshake message"; break; case SSL_ERROR_RX_MALFORMED_CHANGE_CIPHER: cp = "SSL received a malformed Change Cipher Spec record"; break; case SSL_ERROR_RX_MALFORMED_ALERT: cp = "SSL received a malformed Alert record"; break; case SSL_ERROR_RX_MALFORMED_HANDSHAKE: cp = "SSL received a malformed Handshake record"; break; case SSL_ERROR_RX_MALFORMED_APPLICATION_DATA: cp = "SSL received a malformed Application Data record"; break; case SSL_ERROR_RX_UNEXPECTED_HELLO_REQUEST: cp = "SSL received an unexpected Hello Request handshake message"; break; case SSL_ERROR_RX_UNEXPECTED_CLIENT_HELLO: cp = "SSL received an unexpected Client Hello handshake message"; break; case SSL_ERROR_RX_UNEXPECTED_SERVER_HELLO: cp = "SSL received an unexpected Server Hello handshake message"; break; case SSL_ERROR_RX_UNEXPECTED_CERTIFICATE: cp = "SSL received an unexpected Certificate handshake message"; break; case SSL_ERROR_RX_UNEXPECTED_SERVER_KEY_EXCH: cp = "SSL received an unexpected Server Key Exchange handshake message"; break; case SSL_ERROR_RX_UNEXPECTED_CERT_REQUEST: cp = "SSL received an unexpected Certificate Request handshake message"; break; case SSL_ERROR_RX_UNEXPECTED_HELLO_DONE: cp = "SSL received an unexpected Server Hello Done handshake message"; break; case SSL_ERROR_RX_UNEXPECTED_CERT_VERIFY: cp = "SSL received an unexpected Certificate Verify handshake message"; break; case SSL_ERROR_RX_UNEXPECTED_CLIENT_KEY_EXCH: cp = "SSL received an unexpected Client Key Exchange handshake message"; break; case SSL_ERROR_RX_UNEXPECTED_FINISHED: cp = "SSL received an unexpected Finished handshake message"; break; case SSL_ERROR_RX_UNEXPECTED_CHANGE_CIPHER: cp = "SSL received an unexpected Change Cipher Spec record"; break; case SSL_ERROR_RX_UNEXPECTED_ALERT: cp = "SSL received an unexpected Alert record"; break; case SSL_ERROR_RX_UNEXPECTED_HANDSHAKE: cp = "SSL received an unexpected Handshake record"; break; case SSL_ERROR_RX_UNEXPECTED_APPLICATION_DATA: cp = "SSL received an unexpected Application Data record"; break; case SSL_ERROR_RX_UNKNOWN_RECORD_TYPE: cp = "SSL received a record with an unknown content type"; break; case SSL_ERROR_RX_UNKNOWN_HANDSHAKE: cp = "SSL received a handshake message with an unknown message type"; break; case SSL_ERROR_RX_UNKNOWN_ALERT: cp = "SSL received an alert record with an unknown alert description"; break; case SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT: cp = "SSL peer was not expecting a handshake message it received"; break; case SSL_ERROR_DECOMPRESSION_FAILURE_ALERT: cp = "SSL peer was unable to successfully decompress an SSL record it received"; break; case SSL_ERROR_HANDSHAKE_FAILURE_ALERT: cp = "SSL peer was unable to negotiate an acceptable set of security parameters"; break; case SSL_ERROR_ILLEGAL_PARAMETER_ALERT: cp = "SSL peer rejected a handshake message for unacceptable content"; break; case SSL_ERROR_UNSUPPORTED_CERT_ALERT: cp = "SSL peer does not support certificates of the type it received"; break; case SSL_ERROR_CERTIFICATE_UNKNOWN_ALERT: cp = "SSL peer had some unspecified issue with the certificate it received"; break; case SSL_ERROR_DECRYPTION_FAILED_ALERT: cp = "Peer was unable to decrypt an SSL record it received"; break; case SSL_ERROR_RECORD_OVERFLOW_ALERT: cp = "Peer received an SSL record that was longer than is permitted"; break; case SSL_ERROR_UNKNOWN_CA_ALERT: cp = "Peer does not recognize and trust the CA that issued your certificate"; break; case SSL_ERROR_ACCESS_DENIED_ALERT: cp = "Peer received a valid certificate, but access was denied"; break; case SSL_ERROR_DECODE_ERROR_ALERT: cp = "Peer could not decode an SSL handshake message"; break; case SSL_ERROR_DECRYPT_ERROR_ALERT: cp = "Peer reports failure of signature verification or key exchange"; break; case SSL_ERROR_EXPORT_RESTRICTION_ALERT: cp = "Peer reports negotiation not in compliance with export regulations"; break; case SSL_ERROR_PROTOCOL_VERSION_ALERT: cp = "Peer reports incompatible or unsupported protocol version"; break; case SSL_ERROR_INSUFFICIENT_SECURITY_ALERT: cp = "Server requires ciphers more secure than those supported by client"; break; case SSL_ERROR_INTERNAL_ERROR_ALERT: cp = "Peer reports it experienced an internal error"; break; case SSL_ERROR_USER_CANCELED_ALERT: cp = "Peer user canceled handshake"; break; case SSL_ERROR_NO_RENEGOTIATION_ALERT: cp = "Peer does not permit renegotiation of SSL security parameters"; break; case SSL_ERROR_GENERATE_RANDOM_FAILURE: cp = "SSL experienced a failure of its random number generator"; break; case SSL_ERROR_SIGN_HASHES_FAILURE: cp = "Unable to digitally sign data required to verify your certificate"; break; case SSL_ERROR_EXTRACT_PUBLIC_KEY_FAILURE: cp = "SSL was unable to extract the public key from the peer's certificate"; break; case SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE: cp = "Unspecified failure while processing SSL Server Key Exchange handshake"; break; case SSL_ERROR_CLIENT_KEY_EXCHANGE_FAILURE: cp = "Unspecified failure while processing SSL Client Key Exchange handshake"; break; case SSL_ERROR_ENCRYPTION_FAILURE: cp = "Bulk data encryption algorithm failed in selected cipher suite"; break; case SSL_ERROR_DECRYPTION_FAILURE: cp = "Bulk data decryption algorithm failed in selected cipher suite"; break; case SSL_ERROR_MD5_DIGEST_FAILURE: cp = "MD5 digest function failed"; break; case SSL_ERROR_SHA_DIGEST_FAILURE: cp = "SHA-1 digest function failed"; break; case SSL_ERROR_MAC_COMPUTATION_FAILURE: cp = "Message Authentication Code computation failed"; break; case SSL_ERROR_SYM_KEY_CONTEXT_FAILURE: cp = "Failure to create Symmetric Key context"; break; case SSL_ERROR_SYM_KEY_UNWRAP_FAILURE: cp = "Failure to unwrap the Symmetric key in Client Key Exchange message"; break; case SSL_ERROR_IV_PARAM_FAILURE: cp = "PKCS11 code failed to translate an IV into a param"; break; case SSL_ERROR_INIT_CIPHER_SUITE_FAILURE: cp = "Failed to initialize the selected cipher suite"; break; case SSL_ERROR_SOCKET_WRITE_FAILURE: cp = "Attempt to write encrypted data to underlying socket failed"; break; case SSL_ERROR_SESSION_KEY_GEN_FAILURE: cp = "Failed to generate session keys for SSL session"; break; case SEC_ERROR_IO: cp = "An I/O error occurred during authentication"; break; case SEC_ERROR_LIBRARY_FAILURE: cp = "Security library failure"; break; case SEC_ERROR_BAD_DATA: cp = "Security library: received bad data"; break; case SEC_ERROR_OUTPUT_LEN: cp = "Security library: output length error"; break; case SEC_ERROR_INPUT_LEN: cp = "Security library: input length error"; break; case SEC_ERROR_INVALID_ARGS: cp = "Security library: invalid arguments"; break; case SEC_ERROR_INVALID_ALGORITHM: cp = "Security library: invalid algorithm"; break; case SEC_ERROR_INVALID_AVA: cp = "Security library: invalid AVA"; break; case SEC_ERROR_INVALID_TIME: cp = "Security library: invalid time"; break; case SEC_ERROR_BAD_DER: cp = "Security library: improperly formatted DER-encoded message"; break; case SEC_ERROR_BAD_SIGNATURE: cp = "Peer's certificate has an invalid signature"; break; case SEC_ERROR_EXPIRED_CERTIFICATE: cp = "Peer's certificate has expired"; break; case SEC_ERROR_REVOKED_CERTIFICATE: cp = "Peer's certificate has been revoked"; break; case SEC_ERROR_UNKNOWN_ISSUER: cp = "Peer's certificate issuer is not recognized"; break; case SEC_ERROR_BAD_KEY: cp = "Peer's public key is invalid"; break; case SEC_ERROR_BAD_PASSWORD: cp = "The password entered is incorrect"; break; case SEC_ERROR_RETRY_PASSWORD: cp = "New password entered incorrectly"; break; case SEC_ERROR_NO_NODELOCK: cp = "Security library: no nodelock"; break; case SEC_ERROR_BAD_DATABASE: cp = "Security library: bad database"; break; case SEC_ERROR_NO_MEMORY: cp = "Security library: memory allocation failure"; break; case SEC_ERROR_UNTRUSTED_ISSUER: cp = "Peer's certificate issuer has been marked as not trusted by the"; break; case SEC_ERROR_UNTRUSTED_CERT: cp = "Peer's certificate has been marked as not trusted by the user"; break; case SEC_ERROR_DUPLICATE_CERT: cp = "Certificate already exists in your database"; break; case SEC_ERROR_DUPLICATE_CERT_NAME: cp = "Downloaded certificate's name duplicates one already in your"; break; case SEC_ERROR_ADDING_CERT: cp = "Error adding certificate to database"; break; case SEC_ERROR_FILING_KEY: cp = "Error refiling the key for this certificate"; break; case SEC_ERROR_NO_KEY: cp = "The private key for this certificate cannot be found in key"; break; case SEC_ERROR_CERT_VALID: cp = "This certificate is valid"; break; case SEC_ERROR_CERT_NOT_VALID: cp = "This certificate is not valid"; break; case SEC_ERROR_CERT_NO_RESPONSE: cp = "Certificate library: no response"; break; case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE: cp = "The certificate issuer's certificate has expired"; break; case SEC_ERROR_CRL_EXPIRED: cp = "The CRL for the certificate's issuer has expired"; break; case SEC_ERROR_CRL_BAD_SIGNATURE: cp = "The CRL for the certificate's issuer has an invalid signature"; break; case SEC_ERROR_CRL_INVALID: cp = "New CRL has an invalid format"; break; case SEC_ERROR_EXTENSION_VALUE_INVALID: cp = "Certificate extension value is invalid"; break; case SEC_ERROR_EXTENSION_NOT_FOUND: cp = "Certificate extension not found"; break; case SEC_ERROR_CA_CERT_INVALID: cp = "Issuer certificate is invalid"; break; case SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID: cp = "Certificate path length constraint is invalid"; break; case SEC_ERROR_CERT_USAGES_INVALID: cp = "Certificate usages field is invalid"; break; case SEC_INTERNAL_ONLY: cp = "Internal-only module"; break; case SEC_ERROR_INVALID_KEY: cp = "The key does not support the requested operation"; break; case SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION: cp = "Certificate contains unknown critical extension"; break; case SEC_ERROR_OLD_CRL: cp = "New CRL is not later than the current one"; break; case SEC_ERROR_NO_EMAIL_CERT: cp = "Not encrypted or signed: you do not yet have an email certificate"; break; case SEC_ERROR_NO_RECIPIENT_CERTS_QUERY: cp = "Not encrypted: you do not have certificates for each of the"; break; case SEC_ERROR_NOT_A_RECIPIENT: cp = "Cannot decrypt: you are not a recipient, or matching certificate"; break; case SEC_ERROR_PKCS7_KEYALG_MISMATCH: cp = "Cannot decrypt: key encryption algorithm does not match your"; break; case SEC_ERROR_PKCS7_BAD_SIGNATURE: cp = "Signature verification failed: no signer found, too many signers"; break; case SEC_ERROR_UNSUPPORTED_KEYALG: cp = "Unsupported or unknown key algorithm"; break; case SEC_ERROR_DECRYPTION_DISALLOWED: cp = "Cannot decrypt: encrypted using a disallowed algorithm or key size"; break; #ifdef notdef case XP_EC_FORTEZZA_BAD_CARD: cp = "FORTEZZA card has not been properly initialized"; break; case XP_EC_FORTEZZA_NO_CARD: cp = "No FORTEZZA cards found"; break; case XP_EC_FORTEZZA_NONE_SELECTED: cp = "No FORTEZZA card selected"; break; case XP_EC_FORTEZZA_MORE_INFO: cp = "Please select a personality to get more info on"; break; case XP_EC_FORTEZZA_PERSON_NOT_FOUND: cp = "Personality not found"; break; case XP_EC_FORTEZZA_NO_MORE_INFO: cp = "No more information on that personality"; break; case XP_EC_FORTEZZA_BAD_PIN: cp = "Invalid PIN"; break; case XP_EC_FORTEZZA_PERSON_ERROR: cp = "Couldn't initialize FORTEZZA personalities"; break; #endif /* notdef */ case SEC_ERROR_NO_KRL: cp = "No KRL for this site's certificate has been found"; break; case SEC_ERROR_KRL_EXPIRED: cp = "The KRL for this site's certificate has expired"; break; case SEC_ERROR_KRL_BAD_SIGNATURE: cp = "The KRL for this site's certificate has an invalid signature"; break; case SEC_ERROR_REVOKED_KEY: cp = "The key for this site's certificate has been revoked"; break; case SEC_ERROR_KRL_INVALID: cp = "New KRL has an invalid format"; break; case SEC_ERROR_NEED_RANDOM: cp = "Security library: need random data"; break; case SEC_ERROR_NO_MODULE: cp = "Security library: no security module can perform the requested"; break; case SEC_ERROR_NO_TOKEN: cp = "The security card or token does not exist, needs to be"; break; case SEC_ERROR_READ_ONLY: cp = "Security library: read-only database"; break; case SEC_ERROR_NO_SLOT_SELECTED: cp = "No slot or token was selected"; break; case SEC_ERROR_CERT_NICKNAME_COLLISION: cp = "A certificate with the same nickname already exists"; break; case SEC_ERROR_KEY_NICKNAME_COLLISION: cp = "A key with the same nickname already exists"; break; case SEC_ERROR_SAFE_NOT_CREATED: cp = "Error while creating safe object"; break; case SEC_ERROR_BAGGAGE_NOT_CREATED: cp = "Error while creating baggage object"; break; #ifdef notdef case XP_AVA_REMOVE_PRINCIPAL_ERROR: cp = "Couldn't remove the principal"; break; case XP_AVA_DELETE_PRIVILEGE_ERROR: cp = "Couldn't delete the privilege"; break; case XP_AVA_CERT_NOT_EXISTS_ERROR: cp = "This principal doesn't have a certificate"; break; #endif /* notdef */ case SEC_ERROR_BAD_EXPORT_ALGORITHM: cp = "Required algorithm is not allowed"; break; case SEC_ERROR_EXPORTING_CERTIFICATES: cp = "Error attempting to export certificates"; break; case SEC_ERROR_IMPORTING_CERTIFICATES: cp = "Error attempting to import certificates"; break; case SEC_ERROR_PKCS12_DECODING_PFX: cp = "Unable to import. Decoding error. File not valid"; break; case SEC_ERROR_PKCS12_INVALID_MAC: cp = "Unable to import. Invalid MAC. Incorrect password or corrupt file"; break; case SEC_ERROR_PKCS12_UNSUPPORTED_MAC_ALGORITHM: cp = "Unable to import. MAC algorithm not supported"; break; case SEC_ERROR_PKCS12_UNSUPPORTED_TRANSPORT_MODE: cp = "Unable to import. Only password integrity and privacy modes"; break; case SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE: cp = "Unable to import. File structure is corrupt"; break; case SEC_ERROR_PKCS12_UNSUPPORTED_PBE_ALGORITHM: cp = "Unable to import. Encryption algorithm not supported"; break; case SEC_ERROR_PKCS12_UNSUPPORTED_VERSION: cp = "Unable to import. File version not supported"; break; case SEC_ERROR_PKCS12_PRIVACY_PASSWORD_INCORRECT: cp = "Unable to import. Incorrect privacy password"; break; case SEC_ERROR_PKCS12_CERT_COLLISION: cp = "Unable to import. Same nickname already exists in database"; break; case SEC_ERROR_USER_CANCELLED: cp = "The user clicked cancel"; break; case SEC_ERROR_PKCS12_DUPLICATE_DATA: cp = "Not imported, already in database"; break; case SEC_ERROR_MESSAGE_SEND_ABORTED: cp = "Message not sent"; break; case SEC_ERROR_INADEQUATE_KEY_USAGE: cp = "Certificate key usage inadequate for attempted operation"; break; case SEC_ERROR_INADEQUATE_CERT_TYPE: cp = "Certificate type not approved for application"; break; case SEC_ERROR_CERT_ADDR_MISMATCH: cp = "Address in signing certificate does not match address in message"; break; case SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY: cp = "Unable to import. Error attempting to import private key"; break; case SEC_ERROR_PKCS12_IMPORTING_CERT_CHAIN: cp = "Unable to import. Error attempting to import certificate chain"; break; case SEC_ERROR_PKCS12_UNABLE_TO_LOCATE_OBJECT_BY_NAME: cp = "Unable to export. Unable to locate certificate or key by nickname"; break; case SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY: cp = "Unable to export. Private key could not be located and exported"; break; case SEC_ERROR_PKCS12_UNABLE_TO_WRITE: cp = "Unable to export. Unable to write the export file"; break; case SEC_ERROR_PKCS12_UNABLE_TO_READ: cp = "Unable to import. Unable to read the import file"; break; case SEC_ERROR_PKCS12_KEY_DATABASE_NOT_INITIALIZED: cp = "Unable to export. Key database corrupt or deleted"; break; case SEC_ERROR_KEYGEN_FAIL: cp = "Unable to generate public-private key pair"; break; case SEC_ERROR_INVALID_PASSWORD: cp = "Password entered is invalid"; break; case SEC_ERROR_RETRY_OLD_PASSWORD: cp = "Old password entered incorrectly"; break; case SEC_ERROR_BAD_NICKNAME: cp = "Certificate nickname already in use"; break; case SEC_ERROR_NOT_FORTEZZA_ISSUER: cp = "Peer FORTEZZA chain has a non-FORTEZZA Certificate"; break; case SEC_ERROR_CANNOT_MOVE_SENSITIVE_KEY: cp = "A sensitive key cannot be moved to the slot where it is needed"; break; case SEC_ERROR_JS_INVALID_MODULE_NAME: cp = "Invalid module name"; break; case SEC_ERROR_JS_INVALID_DLL: cp = "Invalid module path/filename"; break; case SEC_ERROR_JS_ADD_MOD_FAILURE: cp = "Unable to add module"; break; case SEC_ERROR_JS_DEL_MOD_FAILURE: cp = "Unable to delete module"; break; case SEC_ERROR_OLD_KRL: cp = "New KRL is not later than the current one"; break; case SEC_ERROR_CKL_CONFLICT: cp = "New CKL has different issuer than current CKL"; break; case SEC_ERROR_CERT_NOT_IN_NAME_SPACE: cp = "Certificate issuer is not permitted to issue a certificate with"; break; case SEC_ERROR_KRL_NOT_YET_VALID: cp = "The key revocation list for this certificate is not yet valid"; break; case SEC_ERROR_CRL_NOT_YET_VALID: cp = "The certificate revocation list for this certificate is not yet valid"; break; case SEC_ERROR_UNKNOWN_CERT: cp = "The requested certificate could not be found"; break; case SEC_ERROR_UNKNOWN_SIGNER: cp = "The signer's certificate could not be found"; break; case SEC_ERROR_CERT_BAD_ACCESS_LOCATION: cp = "The location for the certificate status server has invalid format"; break; case SEC_ERROR_OCSP_UNKNOWN_RESPONSE_TYPE: cp = "The OCSP response cannot be fully decoded; it is of an unknown type"; break; case SEC_ERROR_OCSP_BAD_HTTP_RESPONSE: cp = "The OCSP server returned unexpected/invalid HTTP data"; break; case SEC_ERROR_OCSP_MALFORMED_REQUEST: cp = "The OCSP server found the request to be corrupted or improperly formed"; break; case SEC_ERROR_OCSP_SERVER_ERROR: cp = "The OCSP server experienced an internal error"; break; case SEC_ERROR_OCSP_TRY_SERVER_LATER: cp = "The OCSP server suggests trying again later"; break; case SEC_ERROR_OCSP_REQUEST_NEEDS_SIG: cp = "The OCSP server requires a signature on this request"; break; case SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST: cp = "The OCSP server has refused this request as unauthorized"; break; case SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS: cp = "The OCSP server returned an unrecognizable status"; break; case SEC_ERROR_OCSP_UNKNOWN_CERT: cp = "The OCSP server has no status for the certificate"; break; case SEC_ERROR_OCSP_NOT_ENABLED: cp = "You must enable OCSP before performing this operation"; break; case SEC_ERROR_OCSP_NO_DEFAULT_RESPONDER: cp = "You must set the OCSP default responder before performing this operation"; break; case SEC_ERROR_OCSP_MALFORMED_RESPONSE: cp = "The response from the OCSP server was corrupted or improperly formed"; break; case SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE: cp = "The signer of the OCSP response is not authorized to give status for this certificate"; break; case SEC_ERROR_OCSP_FUTURE_RESPONSE: cp = "The OCSP response is not yet valid (contains a date in the future)"; break; case SEC_ERROR_OCSP_OLD_RESPONSE: cp = "The OCSP response contains out-of-date information"; break; case SEC_ERROR_DIGEST_NOT_FOUND: cp = "The CMS or PKCS #7 Digest was not found in signed message"; break; case SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE: cp = "The CMS or PKCS #7 Message type is unsupported"; break; case SEC_ERROR_MODULE_STUCK: cp = "PKCS #11 module could not be removed because it is still in use"; break; case SEC_ERROR_BAD_TEMPLATE: cp = "Could not decode ASN.1 data. Specified template was invalid"; break; case SEC_ERROR_CRL_NOT_FOUND: cp = "No matching CRL was found"; break; case SEC_ERROR_REUSED_ISSUER_AND_SERIAL: cp = "You are attempting to import a cert with the same issuer/serial as an existing cert, but that is not the same cert"; break; case SEC_ERROR_BUSY: cp = "NSS could not shutdown. Objects are still in use"; break; case SEC_ERROR_EXTRA_INPUT: cp = "DER-encoded message contained extra usused data"; break; case SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE: cp = "Unsupported elliptic curve"; break; case SEC_ERROR_UNSUPPORTED_EC_POINT_FORM: cp = "Unsupported elliptic curve point form"; break; case SEC_ERROR_UNRECOGNIZED_OID: cp = "Unrecognized Object IDentifier"; break; case SEC_ERROR_OCSP_INVALID_SIGNING_CERT: cp = "Invalid OCSP signing certificate in OCSP response"; break; #ifdef notdef case SEC_ERROR_REVOKED_CERTIFICATE_CRL: cp = "Certificate is revoked in issuer's certificate revocation list"; break; case SEC_ERROR_REVOKED_CERTIFICATE_OCSP: cp = "Issuer's OCSP responder reports certificate is revoked"; break; case SEC_ERROR_CRL_INVALID_VERSION: cp = "Issuer's Certificate Revocation List has an unknown version number"; break; case SEC_ERROR_CRL_V1_CRITICAL_EXTENSION: cp = "Issuer's V1 Certificate Revocation List has a critical extension"; break; case SEC_ERROR_CRL_UNKNOWN_CRITICAL_EXTENSION: cp = "Issuer's V2 Certificate Revocation List has an unknown critical extension"; break; #endif /* notdef */ } return cp; } heirloom-mailx-12.5/openssl.c000066400000000000000000000700501155563371200162300ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 2002 * Gunnar Ritter. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Gunnar Ritter * and his contributors. * 4. Neither the name of Gunnar Ritter nor the names of his contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)openssl.c 1.26 (gritter) 5/26/09"; #endif #endif /* not lint */ #include "config.h" #ifndef USE_NSS #ifdef USE_OPENSSL #include #include #include static int verbose; static int reset_tio; static struct termios otio; static sigjmp_buf ssljmp; #include #include #include #include #include #include #include #include "rcv.h" #include #include #include #include #include #include #include #ifdef HAVE_ARPA_INET_H #include #endif /* HAVE_ARPA_INET_H */ #include #include "extern.h" /* * Mail -- a mail program * * SSL functions */ /* * OpenSSL client implementation according to: John Viega, Matt Messier, * Pravir Chandra: Network Security with OpenSSL. Sebastopol, CA 2002. */ static int initialized; static int rand_init; static int message_number; static int verify_error_found; static void sslcatch(int s); static int ssl_rand_init(void); static void ssl_init(void); static int ssl_verify_cb(int success, X509_STORE_CTX *store); static const SSL_METHOD *ssl_select_method(const char *uhp); static void ssl_load_verifications(struct sock *sp); static void ssl_certificate(struct sock *sp, const char *uhp); static enum okay ssl_check_host(const char *server, struct sock *sp); #ifdef HAVE_STACK_OF static int smime_verify(struct message *m, int n, STACK_OF(X509) *chain, X509_STORE *store); #else static int smime_verify(struct message *m, int n, STACK *chain, X509_STORE *store); #endif static EVP_CIPHER *smime_cipher(const char *name); static int ssl_password_cb(char *buf, int size, int rwflag, void *userdata); static FILE *smime_sign_cert(const char *xname, const char *xname2, int warn); #if defined (X509_V_FLAG_CRL_CHECK) && defined (X509_V_FLAG_CRL_CHECK_ALL) static enum okay load_crl1(X509_STORE *store, const char *name); #endif static enum okay load_crls(X509_STORE *store, const char *vfile, const char *vdir); static void sslcatch(int s) { if (reset_tio) tcsetattr(0, TCSADRAIN, &otio); siglongjmp(ssljmp, s); } static int ssl_rand_init(void) { char *cp; int state = 0; if ((cp = value("ssl-rand-egd")) != NULL) { cp = expand(cp); if (RAND_egd(cp) == -1) { fprintf(stderr, catgets(catd, CATSET, 245, "entropy daemon at \"%s\" not available\n"), cp); } else state = 1; } else if ((cp = value("ssl-rand-file")) != NULL) { cp = expand(cp); if (RAND_load_file(cp, 1024) == -1) { fprintf(stderr, catgets(catd, CATSET, 246, "entropy file at \"%s\" not available\n"), cp); } else { struct stat st; if (stat(cp, &st) == 0 && S_ISREG(st.st_mode) && access(cp, W_OK) == 0) { if (RAND_write_file(cp) == -1) { fprintf(stderr, catgets(catd, CATSET, 247, "writing entropy data to \"%s\" failed\n"), cp); } } state = 1; } } return state; } static void ssl_init(void) { verbose = value("verbose") != NULL; if (initialized == 0) { SSL_library_init(); initialized = 1; } if (rand_init == 0) rand_init = ssl_rand_init(); } static int ssl_verify_cb(int success, X509_STORE_CTX *store) { if (success == 0) { char data[256]; X509 *cert = X509_STORE_CTX_get_current_cert(store); int depth = X509_STORE_CTX_get_error_depth(store); int err = X509_STORE_CTX_get_error(store); verify_error_found = 1; if (message_number) fprintf(stderr, "Message %d: ", message_number); fprintf(stderr, catgets(catd, CATSET, 229, "Error with certificate at depth: %i\n"), depth); X509_NAME_oneline(X509_get_issuer_name(cert), data, sizeof data); fprintf(stderr, catgets(catd, CATSET, 230, " issuer = %s\n"), data); X509_NAME_oneline(X509_get_subject_name(cert), data, sizeof data); fprintf(stderr, catgets(catd, CATSET, 231, " subject = %s\n"), data); fprintf(stderr, catgets(catd, CATSET, 232, " err %i: %s\n"), err, X509_verify_cert_error_string(err)); if (ssl_vrfy_decide() != OKAY) return 0; } return 1; } static const SSL_METHOD * ssl_select_method(const char *uhp) { const SSL_METHOD *method; char *cp; cp = ssl_method_string(uhp); if (cp != NULL) { if (equal(cp, "ssl2")) method = SSLv2_client_method(); else if (equal(cp, "ssl3")) method = SSLv3_client_method(); else if (equal(cp, "tls1")) method = TLSv1_client_method(); else { fprintf(stderr, catgets(catd, CATSET, 244, "Invalid SSL method \"%s\"\n"), cp); method = SSLv23_client_method(); } } else method = SSLv23_client_method(); return method; } static void ssl_load_verifications(struct sock *sp) { char *ca_dir, *ca_file; X509_STORE *store; if (ssl_vrfy_level == VRFY_IGNORE) return; if ((ca_dir = value("ssl-ca-dir")) != NULL) ca_dir = expand(ca_dir); if ((ca_file = value("ssl-ca-file")) != NULL) ca_file = expand(ca_file); if (ca_dir || ca_file) { if (SSL_CTX_load_verify_locations(sp->s_ctx, ca_file, ca_dir) != 1) { fprintf(stderr, catgets(catd, CATSET, 233, "Error loading")); if (ca_dir) { fprintf(stderr, catgets(catd, CATSET, 234, " %s"), ca_dir); if (ca_file) fprintf(stderr, catgets(catd, CATSET, 235, " or")); } if (ca_file) fprintf(stderr, catgets(catd, CATSET, 236, " %s"), ca_file); fprintf(stderr, catgets(catd, CATSET, 237, "\n")); } } if (value("ssl-no-default-ca") == NULL) { if (SSL_CTX_set_default_verify_paths(sp->s_ctx) != 1) fprintf(stderr, catgets(catd, CATSET, 243, "Error loading default CA locations\n")); } verify_error_found = 0; message_number = 0; SSL_CTX_set_verify(sp->s_ctx, SSL_VERIFY_PEER, ssl_verify_cb); store = SSL_CTX_get_cert_store(sp->s_ctx); load_crls(store, "ssl-crl-file", "ssl-crl-dir"); } static void ssl_certificate(struct sock *sp, const char *uhp) { char *certvar, *keyvar, *cert, *key; certvar = ac_alloc(strlen(uhp) + 10); strcpy(certvar, "ssl-cert-"); strcpy(&certvar[9], uhp); if ((cert = value(certvar)) != NULL || (cert = value("ssl-cert")) != NULL) { cert = expand(cert); if (SSL_CTX_use_certificate_chain_file(sp->s_ctx, cert) == 1) { keyvar = ac_alloc(strlen(uhp) + 9); strcpy(keyvar, "ssl-key-"); if ((key = value(keyvar)) == NULL && (key = value("ssl-key")) == NULL) key = cert; else key = expand(key); if (SSL_CTX_use_PrivateKey_file(sp->s_ctx, key, SSL_FILETYPE_PEM) != 1) fprintf(stderr, catgets(catd, CATSET, 238, "cannot load private key from file %s\n"), key); ac_free(keyvar); } else fprintf(stderr, catgets(catd, CATSET, 239, "cannot load certificate from file %s\n"), cert); } ac_free(certvar); } static enum okay ssl_check_host(const char *server, struct sock *sp) { X509 *cert; X509_NAME *subj; char data[256]; #ifdef HAVE_STACK_OF STACK_OF(GENERAL_NAME) *gens; #else /*GENERAL_NAMES*/STACK *gens; #endif GENERAL_NAME *gen; int i; if ((cert = SSL_get_peer_certificate(sp->s_ssl)) == NULL) { fprintf(stderr, catgets(catd, CATSET, 248, "no certificate from \"%s\"\n"), server); return STOP; } gens = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); if (gens != NULL) { for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) { gen = sk_GENERAL_NAME_value(gens, i); if (gen->type == GEN_DNS) { if (verbose) fprintf(stderr, "Comparing DNS name: \"%s\"\n", gen->d.ia5->data); if (rfc2595_hostname_match(server, (char *)gen->d.ia5->data) == OKAY) goto found; } } } if ((subj = X509_get_subject_name(cert)) != NULL && X509_NAME_get_text_by_NID(subj, NID_commonName, data, sizeof data) > 0) { data[sizeof data - 1] = 0; if (verbose) fprintf(stderr, "Comparing common name: \"%s\"\n", data); if (rfc2595_hostname_match(server, data) == OKAY) goto found; } X509_free(cert); return STOP; found: X509_free(cert); return OKAY; } enum okay ssl_open(const char *server, struct sock *sp, const char *uhp) { char *cp; long options; ssl_init(); ssl_set_vrfy_level(uhp); if ((sp->s_ctx = SSL_CTX_new((SSL_METHOD *)ssl_select_method(uhp))) == NULL) { ssl_gen_err(catgets(catd, CATSET, 261, "SSL_CTX_new() failed")); return STOP; } #ifdef SSL_MODE_AUTO_RETRY /* available with OpenSSL 0.9.6 or later */ SSL_CTX_set_mode(sp->s_ctx, SSL_MODE_AUTO_RETRY); #endif /* SSL_MODE_AUTO_RETRY */ options = SSL_OP_ALL; if (value("ssl-v2-allow") == NULL) options |= SSL_OP_NO_SSLv2; SSL_CTX_set_options(sp->s_ctx, options); ssl_load_verifications(sp); ssl_certificate(sp, uhp); if ((cp = value("ssl-cipher-list")) != NULL) { if (SSL_CTX_set_cipher_list(sp->s_ctx, cp) != 1) fprintf(stderr, catgets(catd, CATSET, 240, "invalid ciphers: %s\n"), cp); } if ((sp->s_ssl = SSL_new(sp->s_ctx)) == NULL) { ssl_gen_err(catgets(catd, CATSET, 262, "SSL_new() failed")); return STOP; } SSL_set_fd(sp->s_ssl, sp->s_fd); if (SSL_connect(sp->s_ssl) < 0) { ssl_gen_err(catgets(catd, CATSET, 263, "could not initiate SSL/TLS connection")); return STOP; } if (ssl_vrfy_level != VRFY_IGNORE) { if (ssl_check_host(server, sp) != OKAY) { fprintf(stderr, catgets(catd, CATSET, 249, "host certificate does not match \"%s\"\n"), server); if (ssl_vrfy_decide() != OKAY) return STOP; } } sp->s_use_ssl = 1; return OKAY; } void ssl_gen_err(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); SSL_load_error_strings(); fprintf(stderr, ": %s\n", (ERR_error_string(ERR_get_error(), NULL))); } FILE * smime_sign(FILE *ip, struct header *headp) { FILE *sp, *fp, *bp, *hp; char *cp, *addr; X509 *cert; PKCS7 *pkcs7; EVP_PKEY *pkey; BIO *bb, *sb; ssl_init(); if ((addr = myorigin(headp)) == NULL) { fprintf(stderr, "No \"from\" address for signing specified\n"); return NULL; } if ((fp = smime_sign_cert(addr, NULL, 1)) == NULL) return NULL; if ((pkey = PEM_read_PrivateKey(fp, NULL, ssl_password_cb, NULL)) == NULL) { ssl_gen_err("Error reading private key from"); Fclose(fp); return NULL; } rewind(fp); if ((cert = PEM_read_X509(fp, NULL, ssl_password_cb, NULL)) == NULL) { ssl_gen_err("Error reading signer certificate from"); Fclose(fp); EVP_PKEY_free(pkey); return NULL; } Fclose(fp); if ((sp = Ftemp(&cp, "Rs", "w+", 0600, 1)) == NULL) { perror("tempfile"); X509_free(cert); EVP_PKEY_free(pkey); return NULL; } rm(cp); Ftfree(&cp); rewind(ip); if (smime_split(ip, &hp, &bp, -1, 0) == STOP) { Fclose(sp); X509_free(cert); EVP_PKEY_free(pkey); return NULL; } if ((bb = BIO_new_fp(bp, BIO_NOCLOSE)) == NULL || (sb = BIO_new_fp(sp, BIO_NOCLOSE)) == NULL) { ssl_gen_err("Error creating BIO signing objects"); Fclose(sp); X509_free(cert); EVP_PKEY_free(pkey); return NULL; } if ((pkcs7 = PKCS7_sign(cert, pkey, NULL, bb, PKCS7_DETACHED)) == NULL) { ssl_gen_err("Error creating the PKCS#7 signing object"); BIO_free(bb); BIO_free(sb); Fclose(sp); X509_free(cert); EVP_PKEY_free(pkey); return NULL; } if (PEM_write_bio_PKCS7(sb, pkcs7) == 0) { ssl_gen_err("Error writing signed S/MIME data"); BIO_free(bb); BIO_free(sb); Fclose(sp); X509_free(cert); EVP_PKEY_free(pkey); return NULL; } BIO_free(bb); BIO_free(sb); X509_free(cert); EVP_PKEY_free(pkey); rewind(bp); fflush(sp); rewind(sp); return smime_sign_assemble(hp, bp, sp); } static int #ifdef HAVE_STACK_OF smime_verify(struct message *m, int n, STACK_OF(X509) *chain, X509_STORE *store) #else smime_verify(struct message *m, int n, STACK *chain, X509_STORE *store) #endif { struct message *x; char *cp, *sender, *to, *cc, *cnttype; int c, i, j; FILE *fp, *ip; off_t size; BIO *fb, *pb; PKCS7 *pkcs7; #ifdef HAVE_STACK_OF STACK_OF(X509) *certs; STACK_OF(GENERAL_NAME) *gens; #else STACK *certs, *gens; #endif X509 *cert; X509_NAME *subj; char data[LINESIZE]; GENERAL_NAME *gen; verify_error_found = 0; message_number = n; loop: sender = getsender(m); to = hfield("to", m); cc = hfield("cc", m); cnttype = hfield("content-type", m); if ((ip = setinput(&mb, m, NEED_BODY)) == NULL) return 1; if (cnttype && strncmp(cnttype, "application/x-pkcs7-mime", 24) == 0) { if ((x = smime_decrypt(m, to, cc, 1)) == NULL) return 1; if (x != (struct message *)-1) { m = x; goto loop; } } size = m->m_size; if ((fp = Ftemp(&cp, "Rv", "w+", 0600, 1)) == NULL) { perror("tempfile"); return 1; } rm(cp); Ftfree(&cp); while (size-- > 0) { c = getc(ip); putc(c, fp); } fflush(fp); rewind(fp); if ((fb = BIO_new_fp(fp, BIO_NOCLOSE)) == NULL) { ssl_gen_err("Error creating BIO verification object " "for message %d", n); Fclose(fp); return 1; } if ((pkcs7 = SMIME_read_PKCS7(fb, &pb)) == NULL) { ssl_gen_err("Error reading PKCS#7 object for message %d", n); BIO_free(fb); Fclose(fp); return 1; } if (PKCS7_verify(pkcs7, chain, store, pb, NULL, 0) != 1) { ssl_gen_err("Error verifying message %d", n); BIO_free(fb); Fclose(fp); return 1; } BIO_free(fb); Fclose(fp); if (sender == NULL) { fprintf(stderr, "Warning: Message %d has no sender.\n", n); return 0; } certs = PKCS7_get0_signers(pkcs7, chain, 0); if (certs == NULL) { fprintf(stderr, "No certificates found in message %d.\n", n); return 1; } for (i = 0; i < sk_X509_num(certs); i++) { cert = sk_X509_value(certs, i); gens = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); if (gens != NULL) { for (j = 0; j < sk_GENERAL_NAME_num(gens); j++) { gen = sk_GENERAL_NAME_value(gens, j); if (gen->type == GEN_EMAIL) { if (verbose) fprintf(stderr, "Comparing alt. " "address: %s\"\n", data); if (!asccasecmp((char *) gen->d.ia5->data, sender)) goto found; } } } if ((subj = X509_get_subject_name(cert)) != NULL && X509_NAME_get_text_by_NID(subj, NID_pkcs9_emailAddress, data, sizeof data) > 0) { data[sizeof data - 1] = 0; if (verbose) fprintf(stderr, "Comparing address: \"%s\"\n", data); if (asccasecmp(data, sender) == 0) goto found; } } fprintf(stderr, "Message %d: certificate does not match <%s>\n", n, sender); return 1; found: if (verify_error_found == 0) printf("Message %d was verified successfully.\n", n); return verify_error_found; } int cverify(void *vp) { int *msgvec = vp, *ip; int ec = 0; #ifdef HAVE_STACK_OF STACK_OF(X509) *chain = NULL; #else STACK *chain = NULL; #endif X509_STORE *store; char *ca_dir, *ca_file; ssl_init(); ssl_vrfy_level = VRFY_STRICT; if ((store = X509_STORE_new()) == NULL) { ssl_gen_err("Error creating X509 store"); return 1; } X509_STORE_set_verify_cb_func(store, ssl_verify_cb); if ((ca_dir = value("smime-ca-dir")) != NULL) ca_dir = expand(ca_dir); if ((ca_file = value("smime-ca-file")) != NULL) ca_file = expand(ca_file); if (ca_dir || ca_file) { if (X509_STORE_load_locations(store, ca_file, ca_dir) != 1) { ssl_gen_err("Error loading %s", ca_file ? ca_file : ca_dir); return 1; } } if (value("smime-no-default-ca") == NULL) { if (X509_STORE_set_default_paths(store) != 1) { ssl_gen_err("Error loading default CA locations"); return 1; } } if (load_crls(store, "smime-crl-file", "smime-crl-dir") != OKAY) return 1; for (ip = msgvec; *ip; ip++) { setdot(&message[*ip-1]); ec |= smime_verify(&message[*ip-1], *ip, chain, store); } return ec; } static EVP_CIPHER * smime_cipher(const char *name) { const EVP_CIPHER *cipher; char *vn, *cp; int vs; vn = ac_alloc(vs = strlen(name) + 30); snprintf(vn, vs, "smime-cipher-%s", name); if ((cp = value(vn)) != NULL) { if (strcmp(cp, "rc2-40") == 0) cipher = EVP_rc2_40_cbc(); else if (strcmp(cp, "rc2-64") == 0) cipher = EVP_rc2_64_cbc(); else if (strcmp(cp, "des") == 0) cipher = EVP_des_cbc(); else if (strcmp(cp, "des-ede3") == 0) cipher = EVP_des_ede3_cbc(); else { fprintf(stderr, "Invalid cipher \"%s\".\n", cp); cipher = NULL; } } else cipher = EVP_des_ede3_cbc(); ac_free(vn); return (EVP_CIPHER *)cipher; } FILE * smime_encrypt(FILE *ip, const char *certfile, const char *to) { FILE *yp, *fp, *bp, *hp; char *cp; X509 *cert; PKCS7 *pkcs7; BIO *bb, *yb; #ifdef HAVE_STACK_OF STACK_OF(X509) *certs; #else STACK *certs; #endif EVP_CIPHER *cipher; certfile = expand((char *)certfile); ssl_init(); if ((cipher = smime_cipher(to)) == NULL) return NULL; if ((fp = Fopen(certfile, "r")) == NULL) { perror(certfile); return NULL; } if ((cert = PEM_read_X509(fp, NULL, ssl_password_cb, NULL)) == NULL) { ssl_gen_err("Error reading encryption certificate from \"%s\"", certfile); Fclose(fp); return NULL; } Fclose(fp); certs = sk_X509_new_null(); sk_X509_push(certs, cert); if ((yp = Ftemp(&cp, "Ry", "w+", 0600, 1)) == NULL) { perror("tempfile"); return NULL; } rm(cp); Ftfree(&cp); rewind(ip); if (smime_split(ip, &hp, &bp, -1, 0) == STOP) { Fclose(yp); return NULL; } if ((bb = BIO_new_fp(bp, BIO_NOCLOSE)) == NULL || (yb = BIO_new_fp(yp, BIO_NOCLOSE)) == NULL) { ssl_gen_err("Error creating BIO encryption objects"); Fclose(yp); return NULL; } if ((pkcs7 = PKCS7_encrypt(certs, bb, cipher, 0)) == NULL) { ssl_gen_err("Error creating the PKCS#7 encryption object"); BIO_free(bb); BIO_free(yb); Fclose(yp); return NULL; } if (PEM_write_bio_PKCS7(yb, pkcs7) == 0) { ssl_gen_err("Error writing encrypted S/MIME data"); BIO_free(bb); BIO_free(yb); Fclose(yp); return NULL; } BIO_free(bb); BIO_free(yb); Fclose(bp); fflush(yp); rewind(yp); return smime_encrypt_assemble(hp, yp); } struct message * smime_decrypt(struct message *m, const char *to, const char *cc, int signcall) { FILE *fp, *bp, *hp, *op; char *cp; X509 *cert = NULL; PKCS7 *pkcs7; EVP_PKEY *pkey = NULL; BIO *bb, *pb, *ob; long size = m->m_size; FILE *yp; if ((yp = setinput(&mb, m, NEED_BODY)) == NULL) return NULL; ssl_init(); if ((fp = smime_sign_cert(to, cc, 0)) != NULL) { if ((pkey = PEM_read_PrivateKey(fp, NULL, ssl_password_cb, NULL)) == NULL) { ssl_gen_err("Error reading private key"); Fclose(fp); return NULL; } rewind(fp); if ((cert = PEM_read_X509(fp, NULL, ssl_password_cb, NULL)) == NULL) { ssl_gen_err("Error reading decryption certificate"); Fclose(fp); EVP_PKEY_free(pkey); return NULL; } Fclose(fp); } if ((op = Ftemp(&cp, "Rp", "w+", 0600, 1)) == NULL) { perror("tempfile"); if (cert) X509_free(cert); if (pkey) EVP_PKEY_free(pkey); return NULL; } rm(cp); Ftfree(&cp); if (smime_split(yp, &hp, &bp, size, 1) == STOP) { Fclose(op); if (cert) X509_free(cert); if (pkey) EVP_PKEY_free(pkey); return NULL; } if ((ob = BIO_new_fp(op, BIO_NOCLOSE)) == NULL || (bb = BIO_new_fp(bp, BIO_NOCLOSE)) == NULL) { ssl_gen_err("Error creating BIO decryption objects"); Fclose(op); if (cert) X509_free(cert); if (pkey) EVP_PKEY_free(pkey); return NULL; } if ((pkcs7 = SMIME_read_PKCS7(bb, &pb)) == NULL) { ssl_gen_err("Error reading PKCS#7 object"); Fclose(op); if (cert) X509_free(cert); if (pkey) EVP_PKEY_free(pkey); return NULL; } if (PKCS7_type_is_signed(pkcs7)) { if (signcall) { BIO_free(bb); BIO_free(ob); if (cert) X509_free(cert); if (pkey) EVP_PKEY_free(pkey); Fclose(op); Fclose(bp); Fclose(hp); setinput(&mb, m, NEED_BODY); return (struct message *)-1; } if (PKCS7_verify(pkcs7, NULL, NULL, NULL, ob, PKCS7_NOVERIFY|PKCS7_NOSIGS) != 1) goto err; fseek(hp, 0L, SEEK_END); fprintf(hp, "X-Encryption-Cipher: none\n"); fflush(hp); rewind(hp); } else if (pkey == NULL) { fprintf(stderr, "No appropriate private key found.\n"); goto err2; } else if (cert == NULL) { fprintf(stderr, "No appropriate certificate found.\n"); goto err2; } else if (PKCS7_decrypt(pkcs7, pkey, cert, ob, 0) != 1) { err: ssl_gen_err("Error decrypting PKCS#7 object"); err2: BIO_free(bb); BIO_free(ob); Fclose(op); Fclose(bp); Fclose(hp); if (cert) X509_free(cert); if (pkey) EVP_PKEY_free(pkey); return NULL; } BIO_free(bb); BIO_free(ob); if (cert) X509_free(cert); if (pkey) EVP_PKEY_free(pkey); fflush(op); rewind(op); Fclose(bp); return smime_decrypt_assemble(m, hp, op); } /*ARGSUSED4*/ static int ssl_password_cb(char *buf, int size, int rwflag, void *userdata) { sighandler_type saveint; char *pass = NULL; int len; (void)&saveint; (void)&pass; saveint = safe_signal(SIGINT, SIG_IGN); if (sigsetjmp(ssljmp, 1) == 0) { if (saveint != SIG_IGN) safe_signal(SIGINT, sslcatch); pass = getpassword(&otio, &reset_tio, "PEM pass phrase:"); } safe_signal(SIGINT, saveint); if (pass == NULL) return 0; len = strlen(pass); if (len > size) len = size; memcpy(buf, pass, len); return len; } static FILE * smime_sign_cert(const char *xname, const char *xname2, int warn) { char *vn, *cp; int vs; FILE *fp; struct name *np; const char *name = xname, *name2 = xname2; loop: if (name) { np = sextract(savestr(name), GTO|GSKIN); while (np) { /* * This needs to be more intelligent since it will * currently take the first name for which a private * key is available regardless of whether it is the * right one for the message. */ vn = ac_alloc(vs = strlen(np->n_name) + 30); snprintf(vn, vs, "smime-sign-cert-%s", np->n_name); if ((cp = value(vn)) != NULL) goto open; np = np->n_flink; } if (name2) { name = name2; name2 = NULL; goto loop; } } if ((cp = value("smime-sign-cert")) != NULL) goto open; if (warn) { fprintf(stderr, "Could not find a certificate for %s", xname); if (xname2) fprintf(stderr, "or %s", xname2); fputc('\n', stderr); } return NULL; open: cp = expand(cp); if ((fp = Fopen(cp, "r")) == NULL) { perror(cp); return NULL; } return fp; } enum okay smime_certsave(struct message *m, int n, FILE *op) { struct message *x; char *cp, *to, *cc, *cnttype; int c, i; FILE *fp, *ip; off_t size; BIO *fb, *pb; PKCS7 *pkcs7; #ifdef HAVE_STACK_OF STACK_OF(X509) *certs; STACK_OF(X509) *chain = NULL; #else STACK *certs; STACK *chain = NULL; #endif X509 *cert; enum okay ok = OKAY; message_number = n; loop: to = hfield("to", m); cc = hfield("cc", m); cnttype = hfield("content-type", m); if ((ip = setinput(&mb, m, NEED_BODY)) == NULL) return STOP; if (cnttype && strncmp(cnttype, "application/x-pkcs7-mime", 24) == 0) { if ((x = smime_decrypt(m, to, cc, 1)) == NULL) return STOP; if (x != (struct message *)-1) { m = x; goto loop; } } size = m->m_size; if ((fp = Ftemp(&cp, "Rv", "w+", 0600, 1)) == NULL) { perror("tempfile"); return STOP; } rm(cp); Ftfree(&cp); while (size-- > 0) { c = getc(ip); putc(c, fp); } fflush(fp); rewind(fp); if ((fb = BIO_new_fp(fp, BIO_NOCLOSE)) == NULL) { ssl_gen_err("Error creating BIO object for message %d", n); Fclose(fp); return STOP; } if ((pkcs7 = SMIME_read_PKCS7(fb, &pb)) == NULL) { ssl_gen_err("Error reading PKCS#7 object for message %d", n); BIO_free(fb); Fclose(fp); return STOP; } BIO_free(fb); Fclose(fp); certs = PKCS7_get0_signers(pkcs7, chain, 0); if (certs == NULL) { fprintf(stderr, "No certificates found in message %d.\n", n); return STOP; } for (i = 0; i < sk_X509_num(certs); i++) { cert = sk_X509_value(certs, i); if (X509_print_fp(op, cert) == 0 || PEM_write_X509(op, cert) == 0) { ssl_gen_err("Error writing certificate %d from " "message %d", i, n); ok = STOP; } } return ok; } #if defined (X509_V_FLAG_CRL_CHECK) && defined (X509_V_FLAG_CRL_CHECK_ALL) static enum okay load_crl1(X509_STORE *store, const char *name) { X509_LOOKUP *lookup; if (verbose) printf("Loading CRL from \"%s\".\n", name); if ((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) == NULL) { ssl_gen_err("Error creating X509 lookup object"); return STOP; } if (X509_load_crl_file(lookup, name, X509_FILETYPE_PEM) != 1) { ssl_gen_err("Error loading CRL from \"%s\"", name); return STOP; } return OKAY; } #endif /* new OpenSSL */ static enum okay load_crls(X509_STORE *store, const char *vfile, const char *vdir) { char *crl_file, *crl_dir; #if defined (X509_V_FLAG_CRL_CHECK) && defined (X509_V_FLAG_CRL_CHECK_ALL) DIR *dirfd; struct dirent *dp; char *fn = NULL; int fs = 0, ds, es; #endif /* new OpenSSL */ if ((crl_file = value(vfile)) != NULL) { #if defined (X509_V_FLAG_CRL_CHECK) && defined (X509_V_FLAG_CRL_CHECK_ALL) crl_file = expand(crl_file); if (load_crl1(store, crl_file) != OKAY) return STOP; #else /* old OpenSSL */ fprintf(stderr, "This OpenSSL version is too old to use CRLs.\n"); return STOP; #endif /* old OpenSSL */ } if ((crl_dir = value(vdir)) != NULL) { #if defined (X509_V_FLAG_CRL_CHECK) && defined (X509_V_FLAG_CRL_CHECK_ALL) crl_dir = expand(crl_dir); ds = strlen(crl_dir); if ((dirfd = opendir(crl_dir)) == NULL) { perror(crl_dir); return STOP; } fn = smalloc(fs = ds + 20); strcpy(fn, crl_dir); fn[ds] = '/'; while ((dp = readdir(dirfd)) != NULL) { if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) continue; if (dp->d_name[0] == '.') continue; if (ds + (es = strlen(dp->d_name)) + 2 < fs) fn = srealloc(fn, fs = ds + es + 20); strcpy(&fn[ds+1], dp->d_name); if (load_crl1(store, fn) != OKAY) { closedir(dirfd); free(fn); return STOP; } } closedir(dirfd); free(fn); #else /* old OpenSSL */ fprintf(stderr, "This OpenSSL version is too old to use CRLs.\n"); return STOP; #endif /* old OpenSSL */ } #if defined (X509_V_FLAG_CRL_CHECK) && defined (X509_V_FLAG_CRL_CHECK_ALL) if (crl_file || crl_dir) X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); #endif /* old OpenSSL */ return OKAY; } #else /* !USE_OPENSSL */ #include static void nosmime(void) { fprintf(stderr, "No S/MIME support compiled in.\n"); } /*ARGSUSED*/ FILE * smime_sign(FILE *fp) { nosmime(); return NULL; } /*ARGSUSED*/ int cverify(void *vp) { nosmime(); return 1; } /*ARGSUSED*/ FILE * smime_encrypt(FILE *fp, const char *certfile, const char *to) { nosmime(); return NULL; } /*ARGSUSED*/ struct message * smime_decrypt(struct message *m, const char *to, const char *cc, int signcall) { nosmime(); return NULL; } /*ARGSUSED*/ int ccertsave(void *v) { nosmime(); return 1; } #endif /* !USE_OPENSSL */ #endif /* !USE_NSS */ heirloom-mailx-12.5/pkginfo000066400000000000000000000003731155563371200157620ustar00rootroot00000000000000# Sccsid @(#)pkginfo 1.2 (gritter) 2/25/07 PKG=mailx NAME=Heirloom mailx DESC=A modernized variant of mailx. VENDOR=Gunnar Ritter HOTLINE=http://heirloom.sourceforge.net EMAIL=gunnarr@acm.org VERSION=070225 ARCH=IA32,i386 CATEGORY=utilities BASEDIR=/ heirloom-mailx-12.5/pop3.c000066400000000000000000000522231155563371200154300ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 2002 * Gunnar Ritter. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Gunnar Ritter * and his contributors. * 4. Neither the name of Gunnar Ritter nor the names of his contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)pop3.c 2.43 (gritter) 3/4/06"; #endif #endif /* not lint */ #include "config.h" #include "rcv.h" #include "extern.h" #include #include #include #include #include "md5.h" /* * Mail -- a mail program * * POP3 client. */ #ifdef HAVE_SOCKETS static int verbose; #define POP3_ANSWER() if (pop3_answer(mp) == STOP) \ return STOP; #define POP3_OUT(x, y) if (pop3_finish(mp) == STOP) \ return STOP; \ if (verbose) \ fprintf(stderr, ">>> %s", x); \ mp->mb_active |= (y); \ if (swrite(&mp->mb_sock, x) == STOP) \ return STOP; static char *pop3buf; static size_t pop3bufsize; static sigjmp_buf pop3jmp; static sighandler_type savealrm; static int reset_tio; static struct termios otio; static int pop3keepalive; static volatile int pop3lock; static void pop3_timer_off(void); static enum okay pop3_answer(struct mailbox *mp); static enum okay pop3_finish(struct mailbox *mp); static void pop3catch(int s); static void maincatch(int s); static enum okay pop3_noop1(struct mailbox *mp); static void pop3alarm(int s); static enum okay pop3_pass(struct mailbox *mp, const char *pass); static char *pop3_find_timestamp(const char *bp); static enum okay pop3_apop(struct mailbox *mp, char *xuser, const char *pass, const char *ts); static enum okay pop3_apop1(struct mailbox *mp, const char *user, const char *xp); static int pop3_use_starttls(const char *uhp); static int pop3_use_apop(const char *uhp); static enum okay pop3_user(struct mailbox *mp, char *xuser, const char *pass, const char *uhp, const char *xserver); static enum okay pop3_stat(struct mailbox *mp, off_t *size, int *count); static enum okay pop3_list(struct mailbox *mp, int n, size_t *size); static void pop3_init(struct mailbox *mp, int n); static void pop3_dates(struct mailbox *mp); static void pop3_setptr(struct mailbox *mp); static char *pop3_have_password(const char *server); static enum okay pop3_get(struct mailbox *mp, struct message *m, enum needspec need); static enum okay pop3_exit(struct mailbox *mp); static enum okay pop3_delete(struct mailbox *mp, int n); static enum okay pop3_update(struct mailbox *mp); static void pop3_timer_off(void) { if (pop3keepalive > 0) { alarm(0); safe_signal(SIGALRM, savealrm); } } static enum okay pop3_answer(struct mailbox *mp) { int sz; enum okay ok = STOP; retry: if ((sz = sgetline(&pop3buf, &pop3bufsize, NULL, &mp->mb_sock)) > 0) { if ((mp->mb_active & (MB_COMD|MB_MULT)) == MB_MULT) goto multiline; if (verbose) fputs(pop3buf, stderr); switch (*pop3buf) { case '+': ok = OKAY; mp->mb_active &= ~MB_COMD; break; case '-': ok = STOP; mp->mb_active = MB_NONE; fprintf(stderr, catgets(catd, CATSET, 218, "POP3 error: %s"), pop3buf); break; default: /* * If the answer starts neither with '+' nor with * '-', it must be part of a multiline response, * e. g. because the user interrupted a file * download. Get lines until a single dot appears. */ multiline: while (pop3buf[0] != '.' || pop3buf[1] != '\r' || pop3buf[2] != '\n' || pop3buf[3] != '\0') { sz = sgetline(&pop3buf, &pop3bufsize, NULL, &mp->mb_sock); if (sz <= 0) goto eof; } mp->mb_active &= ~MB_MULT; if (mp->mb_active != MB_NONE) goto retry; } } else { eof: ok = STOP; mp->mb_active = MB_NONE; } return ok; } static enum okay pop3_finish(struct mailbox *mp) { while (mp->mb_sock.s_fd > 0 && mp->mb_active != MB_NONE) pop3_answer(mp); return OKAY; } static void pop3catch(int s) { if (reset_tio) tcsetattr(0, TCSADRAIN, &otio); switch (s) { case SIGINT: fprintf(stderr, catgets(catd, CATSET, 102, "Interrupt\n")); siglongjmp(pop3jmp, 1); break; case SIGPIPE: fprintf(stderr, "Received SIGPIPE during POP3 operation\n"); break; } } static void maincatch(int s) { if (interrupts++ == 0) { fprintf(stderr, catgets(catd, CATSET, 102, "Interrupt\n")); return; } onintr(0); } static enum okay pop3_noop1(struct mailbox *mp) { POP3_OUT("NOOP\r\n", MB_COMD) POP3_ANSWER() return OKAY; } enum okay pop3_noop(void) { enum okay ok = STOP; sighandler_type saveint, savepipe; (void)&saveint; (void)&savepipe; (void)&ok; verbose = value("verbose") != NULL; pop3lock = 1; if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN) safe_signal(SIGINT, maincatch); savepipe = safe_signal(SIGPIPE, SIG_IGN); if (sigsetjmp(pop3jmp, 1) == 0) { if (savepipe != SIG_IGN) safe_signal(SIGPIPE, pop3catch); ok = pop3_noop1(&mb); } safe_signal(SIGINT, saveint); safe_signal(SIGPIPE, savepipe); pop3lock = 0; return ok; } /*ARGSUSED*/ static void pop3alarm(int s) { sighandler_type saveint; sighandler_type savepipe; if (pop3lock++ == 0) { if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN) safe_signal(SIGINT, maincatch); savepipe = safe_signal(SIGPIPE, SIG_IGN); if (sigsetjmp(pop3jmp, 1)) { safe_signal(SIGINT, saveint); safe_signal(SIGPIPE, savepipe); goto brk; } if (savepipe != SIG_IGN) safe_signal(SIGPIPE, pop3catch); if (pop3_noop1(&mb) != OKAY) { safe_signal(SIGINT, saveint); safe_signal(SIGPIPE, savepipe); goto out; } safe_signal(SIGINT, saveint); safe_signal(SIGPIPE, savepipe); } brk: alarm(pop3keepalive); out: pop3lock--; } static enum okay pop3_pass(struct mailbox *mp, const char *pass) { char o[LINESIZE]; snprintf(o, sizeof o, "PASS %s\r\n", pass); POP3_OUT(o, MB_COMD) POP3_ANSWER() return OKAY; } static char * pop3_find_timestamp(const char *bp) { const char *cp, *ep; char *rp; int hadat = 0; if ((cp = strchr(bp, '<')) == NULL) return NULL; for (ep = cp; *ep; ep++) { if (spacechar(*ep&0377)) return NULL; else if (*ep == '@') hadat = 1; else if (*ep == '>') { if (hadat != 1) return NULL; break; } } if (*ep != '>') return NULL; rp = salloc(ep - cp + 2); memcpy(rp, cp, ep - cp + 1); rp[ep - cp + 1] = '\0'; return rp; } static enum okay pop3_apop(struct mailbox *mp, char *xuser, const char *pass, const char *ts) { char *user, *catp, *xp; unsigned char digest[16]; MD5_CTX ctx; retry: if (xuser == NULL) { if ((user = getuser()) == NULL) return STOP; } else user = xuser; if (pass == NULL) { if ((pass = getpassword(&otio, &reset_tio, NULL)) == NULL) return STOP; } catp = savecat(ts, pass); MD5Init(&ctx); MD5Update(&ctx, (unsigned char *)catp, strlen(catp)); MD5Final(digest, &ctx); xp = md5tohex(digest); if (pop3_apop1(mp, user, xp) == STOP) { pass = NULL; goto retry; } return OKAY; } static enum okay pop3_apop1(struct mailbox *mp, const char *user, const char *xp) { char o[LINESIZE]; snprintf(o, sizeof o, "APOP %s %s\r\n", user, xp); POP3_OUT(o, MB_COMD) POP3_ANSWER() return OKAY; } static int pop3_use_starttls(const char *uhp) { char *var; if (value("pop3-use-starttls")) return 1; var = savecat("pop3-use-starttls-", uhp); return value(var) != NULL; } static int pop3_use_apop(const char *uhp) { char *var; if (value("pop3-use-apop")) return 1; var = savecat("pop3-use-apop-", uhp); return value(var) != NULL; } static enum okay pop3_user(struct mailbox *mp, char *xuser, const char *pass, const char *uhp, const char *xserver) { char o[LINESIZE], *user, *ts = NULL, *server, *cp; POP3_ANSWER() if (pop3_use_apop(uhp)) { if ((ts = pop3_find_timestamp(pop3buf)) == NULL) { fprintf(stderr, "Could not determine timestamp from " "server greeting. Impossible to use APOP.\n"); return STOP; } } if ((cp = strchr(xserver, ':')) != NULL) { server = salloc(cp - xserver + 1); memcpy(server, xserver, cp - xserver); server[cp - xserver] = '\0'; } else server = (char *)xserver; #ifdef USE_SSL if (mp->mb_sock.s_use_ssl == 0 && pop3_use_starttls(uhp)) { POP3_OUT("STLS\r\n", MB_COMD) POP3_ANSWER() if (ssl_open(server, &mp->mb_sock, uhp) != OKAY) return STOP; } #else /* !USE_SSL */ if (pop3_use_starttls(uhp)) { fprintf(stderr, "No SSL support compiled in.\n"); return STOP; } #endif /* !USE_SSL */ if (ts != NULL) return pop3_apop(mp, xuser, pass, ts); retry: if (xuser == NULL) { if ((user = getuser()) == NULL) return STOP; } else user = xuser; snprintf(o, sizeof o, "USER %s\r\n", user); POP3_OUT(o, MB_COMD) POP3_ANSWER() if (pass == NULL) { if ((pass = getpassword(&otio, &reset_tio, NULL)) == NULL) return STOP; } if (pop3_pass(mp, pass) == STOP) { pass = NULL; goto retry; } return OKAY; } static enum okay pop3_stat(struct mailbox *mp, off_t *size, int *count) { char *cp; enum okay ok = OKAY; POP3_OUT("STAT\r\n", MB_COMD); POP3_ANSWER() for (cp = pop3buf; *cp && !spacechar(*cp & 0377); cp++); while (*cp && spacechar(*cp & 0377)) cp++; if (*cp) { *count = (int)strtol(cp, NULL, 10); while (*cp && !spacechar(*cp & 0377)) cp++; while (*cp && spacechar(*cp & 0377)) cp++; if (*cp) *size = (int)strtol(cp, NULL, 10); else ok = STOP; } else ok = STOP; if (ok == STOP) fprintf(stderr, catgets(catd, CATSET, 260, "invalid POP3 STAT response: %s\n"), pop3buf); return ok; } static enum okay pop3_list(struct mailbox *mp, int n, size_t *size) { char o[LINESIZE], *cp; snprintf(o, sizeof o, "LIST %u\r\n", n); POP3_OUT(o, MB_COMD) POP3_ANSWER() for (cp = pop3buf; *cp && !spacechar(*cp & 0377); cp++); while (*cp && spacechar(*cp & 0377)) cp++; while (*cp && !spacechar(*cp & 0377)) cp++; while (*cp && spacechar(*cp & 0377)) cp++; if (*cp) *size = (size_t)strtol(cp, NULL, 10); else *size = 0; return OKAY; } static void pop3_init(struct mailbox *mp, int n) { struct message *m = &message[n]; char *cp; m->m_flag = MUSED|MNEW|MNOFROM|MNEWEST; m->m_block = 0; m->m_offset = 0; pop3_list(mp, m - message + 1, &m->m_xsize); if ((cp = hfield("status", m)) != NULL) { while (*cp != '\0') { if (*cp == 'R') m->m_flag |= MREAD; else if (*cp == 'O') m->m_flag &= ~MNEW; cp++; } } } /*ARGSUSED*/ static void pop3_dates(struct mailbox *mp) { int i; for (i = 0; i < msgCount; i++) substdate(&message[i]); } static void pop3_setptr(struct mailbox *mp) { int i; message = scalloc(msgCount + 1, sizeof *message); for (i = 0; i < msgCount; i++) pop3_init(mp, i); setdot(message); message[msgCount].m_size = 0; message[msgCount].m_lines = 0; pop3_dates(mp); } static char * pop3_have_password(const char *server) { char *var, *cp; var = ac_alloc(strlen(server) + 10); strcpy(var, "password-"); strcpy(&var[9], server); if ((cp = value(var)) != NULL) cp = savestr(cp); ac_free(var); return cp; } int pop3_setfile(const char *server, int newmail, int isedit) { struct sock so; sighandler_type saveint; sighandler_type savepipe; char *user; const char *cp, *sp = server, *pass, *uhp; int use_ssl = 0; (void)&sp; (void)&use_ssl; (void)&user; if (newmail) return 1; if (strncmp(sp, "pop3://", 7) == 0) { sp = &sp[7]; use_ssl = 0; #ifdef USE_SSL } else if (strncmp(sp, "pop3s://", 8) == 0) { sp = &sp[8]; use_ssl = 1; #endif /* USE_SSL */ } uhp = sp; pass = pop3_have_password(uhp); if ((cp = last_at_before_slash(sp)) != NULL) { user = salloc(cp - sp + 1); memcpy(user, sp, cp - sp); user[cp - sp] = '\0'; sp = &cp[1]; user = strdec(user); } else user = NULL; verbose = value("verbose") != NULL; if (sopen(sp, &so, use_ssl, uhp, use_ssl ? "pop3s" : "pop3", verbose) != OKAY) { return -1; } quit(); edit = isedit; if (mb.mb_sock.s_fd >= 0) sclose(&mb.mb_sock); if (mb.mb_itf) { fclose(mb.mb_itf); mb.mb_itf = NULL; } if (mb.mb_otf) { fclose(mb.mb_otf); mb.mb_otf = NULL; } initbox(server); mb.mb_type = MB_VOID; pop3lock = 1; mb.mb_sock = so; saveint = safe_signal(SIGINT, SIG_IGN); savepipe = safe_signal(SIGPIPE, SIG_IGN); if (sigsetjmp(pop3jmp, 1)) { sclose(&mb.mb_sock); safe_signal(SIGINT, saveint); safe_signal(SIGPIPE, savepipe); pop3lock = 0; return 1; } if (saveint != SIG_IGN) safe_signal(SIGINT, pop3catch); if (savepipe != SIG_IGN) safe_signal(SIGPIPE, pop3catch); if ((cp = value("pop3-keepalive")) != NULL) { if ((pop3keepalive = strtol(cp, NULL, 10)) > 0) { savealrm = safe_signal(SIGALRM, pop3alarm); alarm(pop3keepalive); } } mb.mb_sock.s_desc = "POP3"; mb.mb_sock.s_onclose = pop3_timer_off; if (pop3_user(&mb, user, pass, uhp, sp) != OKAY || pop3_stat(&mb, &mailsize, &msgCount) != OKAY) { sclose(&mb.mb_sock); pop3_timer_off(); safe_signal(SIGINT, saveint); safe_signal(SIGPIPE, savepipe); pop3lock = 0; return 1; } mb.mb_type = MB_POP3; mb.mb_perm = Rflag ? 0 : MB_DELE; pop3_setptr(&mb); setmsize(msgCount); sawcom = 0; safe_signal(SIGINT, saveint); safe_signal(SIGPIPE, savepipe); pop3lock = 0; if (!edit && msgCount == 0) { if (mb.mb_type == MB_POP3 && value("emptystart") == NULL) fprintf(stderr, catgets(catd, CATSET, 258, "No mail at %s\n"), server); return 1; } return 0; } static enum okay pop3_get(struct mailbox *mp, struct message *m, enum needspec need) { sighandler_type saveint = SIG_IGN; sighandler_type savepipe = SIG_IGN; off_t offset; char o[LINESIZE], *line = NULL, *lp; size_t linesize = 0, linelen, size; int number = m - message + 1; int emptyline = 0, lines; (void)&saveint; (void)&savepipe; (void)&number; (void)&emptyline; (void)&need; verbose = value("verbose") != NULL; if (mp->mb_sock.s_fd < 0) { fprintf(stderr, catgets(catd, CATSET, 219, "POP3 connection already closed.\n")); return STOP; } if (pop3lock++ == 0) { if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN) safe_signal(SIGINT, maincatch); savepipe = safe_signal(SIGPIPE, SIG_IGN); if (sigsetjmp(pop3jmp, 1)) { safe_signal(SIGINT, saveint); safe_signal(SIGPIPE, savepipe); pop3lock--; return STOP; } if (savepipe != SIG_IGN) safe_signal(SIGPIPE, pop3catch); } fseek(mp->mb_otf, 0L, SEEK_END); offset = ftell(mp->mb_otf); retry: switch (need) { case NEED_HEADER: snprintf(o, sizeof o, "TOP %u 0\r\n", number); break; case NEED_BODY: snprintf(o, sizeof o, "RETR %u\r\n", number); break; case NEED_UNSPEC: abort(); } POP3_OUT(o, MB_COMD|MB_MULT) if (pop3_answer(mp) == STOP) { if (need == NEED_HEADER) { /* * The TOP POP3 command is optional, so retry * with the entire message. */ need = NEED_BODY; goto retry; } if (interrupts) onintr(0); return STOP; } size = 0; lines = 0; while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) { if (line[0] == '.' && line[1] == '\r' && line[2] == '\n' && line[3] == '\0') { mp->mb_active &= ~MB_MULT; break; } if (line[0] == '.') { lp = &line[1]; linelen--; } else lp = line; /* * Need to mask 'From ' lines. This cannot be done properly * since some servers pass them as 'From ' and others as * '>From '. Although one could identify the first kind of * server in principle, it is not possible to identify the * second as '>From ' may also come from a server of the * first type as actual data. So do what is absolutely * necessary only - mask 'From '. * * If the line is the first line of the message header, it * is likely a real 'From ' line. In this case, it is just * ignored since it violates all standards. */ if (lp[0] == 'F' && lp[1] == 'r' && lp[2] == 'o' && lp[3] == 'm' && lp[4] == ' ') { if (lines != 0) { fputc('>', mp->mb_otf); size++; } else continue; } lines++; if (lp[linelen-1] == '\n' && (linelen == 1 || lp[linelen-2] == '\r')) { emptyline = linelen <= 2; if (linelen > 2) fwrite(lp, 1, linelen - 2, mp->mb_otf); fputc('\n', mp->mb_otf); size += linelen - 1; } else { emptyline = 0; fwrite(lp, 1, linelen, mp->mb_otf); size += linelen; } } if (!emptyline) { /* * This is very ugly; but some POP3 daemons don't end a * message with \r\n\r\n, and we need \n\n for mbox format. */ fputc('\n', mp->mb_otf); lines++; size++; } m->m_size = size; m->m_lines = lines; m->m_block = mailx_blockof(offset); m->m_offset = mailx_offsetof(offset); fflush(mp->mb_otf); switch (need) { case NEED_HEADER: m->m_have |= HAVE_HEADER; break; case NEED_BODY: m->m_have |= HAVE_HEADER|HAVE_BODY; m->m_xlines = m->m_lines; m->m_xsize = m->m_size; break; case NEED_UNSPEC: break; } if (line) free(line); if (saveint != SIG_IGN) safe_signal(SIGINT, saveint); if (savepipe != SIG_IGN) safe_signal(SIGPIPE, savepipe); pop3lock--; if (interrupts) onintr(0); return OKAY; } enum okay pop3_header(struct message *m) { return pop3_get(&mb, m, NEED_HEADER); } enum okay pop3_body(struct message *m) { return pop3_get(&mb, m, NEED_BODY); } static enum okay pop3_exit(struct mailbox *mp) { POP3_OUT("QUIT\r\n", MB_COMD) POP3_ANSWER() return OKAY; } static enum okay pop3_delete(struct mailbox *mp, int n) { char o[LINESIZE]; snprintf(o, sizeof o, "DELE %u\r\n", n); POP3_OUT(o, MB_COMD) POP3_ANSWER() return OKAY; } static enum okay pop3_update(struct mailbox *mp) { FILE *readstat = NULL; struct message *m; int dodel, c, gotcha, held; if (Tflag != NULL) { if ((readstat = Zopen(Tflag, "w", NULL)) == NULL) Tflag = NULL; } if (!edit) { holdbits(); for (m = &message[0], c = 0; m < &message[msgCount]; m++) { if (m->m_flag & MBOX) c++; } if (c > 0) makembox(); } for (m = &message[0], gotcha=0, held=0; m < &message[msgCount]; m++) { if (readstat != NULL && (m->m_flag & (MREAD|MDELETED)) != 0) { char *id; if ((id = hfield("message-id", m)) != NULL || (id = hfield("article-id", m)) != NULL) fprintf(readstat, "%s\n", id); } if (edit) { dodel = m->m_flag & MDELETED; } else { dodel = !((m->m_flag&MPRESERVE) || (m->m_flag&MTOUCH) == 0); } if (dodel) { pop3_delete(mp, m - message + 1); gotcha++; } else held++; } if (readstat != NULL) Fclose(readstat); if (gotcha && edit) { printf(catgets(catd, CATSET, 168, "\"%s\" "), mailname); printf(value("bsdcompat") || value("bsdmsgs") ? catgets(catd, CATSET, 170, "complete\n") : catgets(catd, CATSET, 212, "updated.\n")); } else if (held && !edit) { if (held == 1) printf(catgets(catd, CATSET, 155, "Held 1 message in %s\n"), mailname); else if (held > 1) printf(catgets(catd, CATSET, 156, "Held %d messages in %s\n"), held, mailname); } fflush(stdout); return OKAY; } void pop3_quit(void) { sighandler_type saveint; sighandler_type savepipe; verbose = value("verbose") != NULL; if (mb.mb_sock.s_fd < 0) { fprintf(stderr, catgets(catd, CATSET, 219, "POP3 connection already closed.\n")); return; } pop3lock = 1; saveint = safe_signal(SIGINT, SIG_IGN); savepipe = safe_signal(SIGPIPE, SIG_IGN); if (sigsetjmp(pop3jmp, 1)) { safe_signal(SIGINT, saveint); safe_signal(SIGPIPE, saveint); pop3lock = 0; return; } if (saveint != SIG_IGN) safe_signal(SIGINT, pop3catch); if (savepipe != SIG_IGN) safe_signal(SIGPIPE, pop3catch); pop3_update(&mb); pop3_exit(&mb); sclose(&mb.mb_sock); safe_signal(SIGINT, saveint); safe_signal(SIGPIPE, savepipe); pop3lock = 0; } #else /* !HAVE_SOCKETS */ static void nopop3(void) { fprintf(stderr, catgets(catd, CATSET, 216, "No POP3 support compiled in.\n")); } int pop3_setfile(const char *server, int newmail, int isedit) { nopop3(); return -1; } enum okay pop3_header(struct message *mp) { nopop3(); return STOP; } enum okay pop3_body(struct message *mp) { nopop3(); return STOP; } void pop3_quit(void) { nopop3(); } enum okay pop3_noop(void) { nopop3(); return STOP; } #endif /* HAVE_SOCKETS */ heirloom-mailx-12.5/popen.c000066400000000000000000000363031155563371200156710ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)popen.c 2.20 (gritter) 3/4/06"; #endif #endif /* not lint */ #include "rcv.h" #include "extern.h" #include #include #include #include #include #ifndef NSIG #define NSIG 32 #endif #define READ 0 #define WRITE 1 struct fp { FILE *fp; struct fp *link; char *realfile; long offset; int omode; int pipe; int pid; enum { FP_UNCOMPRESSED = 00, FP_GZIPPED = 01, FP_BZIP2ED = 02, FP_IMAP = 03, FP_MAILDIR = 04, FP_MASK = 0177, FP_READONLY = 0200 } compressed; }; static struct fp *fp_head; struct child { int pid; char done; char free; int status; struct child *link; }; static struct child *child; static int scan_mode(const char *mode, int *omode); static void register_file(FILE *fp, int omode, int pipe, int pid, int compressed, const char *realfile, long offset); static enum okay compress(struct fp *fpp); static int decompress(int compression, int input, int output); static enum okay unregister_file(FILE *fp); static int file_pid(FILE *fp); static int wait_command(int pid); static struct child *findchild(int pid); static void delchild(struct child *cp); /* * Provide BSD-like signal() on all systems. */ sighandler_type safe_signal(int signum, sighandler_type handler) { struct sigaction nact, oact; nact.sa_handler = handler; sigemptyset(&nact.sa_mask); nact.sa_flags = 0; #ifdef SA_RESTART nact.sa_flags |= SA_RESTART; #endif if (sigaction(signum, &nact, &oact) != 0) return SIG_ERR; return oact.sa_handler; } static int scan_mode(const char *mode, int *omode) { if (!strcmp(mode, "r")) { *omode = O_RDONLY; } else if (!strcmp(mode, "w")) { *omode = O_WRONLY | O_CREAT | O_TRUNC; } else if (!strcmp(mode, "wx")) { *omode = O_WRONLY | O_CREAT | O_EXCL; } else if (!strcmp(mode, "a")) { *omode = O_WRONLY | O_APPEND | O_CREAT; } else if (!strcmp(mode, "a+")) { *omode = O_RDWR | O_APPEND; } else if (!strcmp(mode, "r+")) { *omode = O_RDWR; } else if (!strcmp(mode, "w+")) { *omode = O_RDWR | O_CREAT | O_EXCL; } else { fprintf(stderr, catgets(catd, CATSET, 152, "Internal error: bad stdio open mode %s\n"), mode); errno = EINVAL; return -1; } return 0; } FILE * safe_fopen(const char *file, const char *mode, int *omode) { int fd; if (scan_mode(mode, omode) < 0) return NULL; if ((fd = open(file, *omode, 0666)) < 0) return NULL; return fdopen(fd, mode); } FILE * Fopen(const char *file, const char *mode) { FILE *fp; int omode; if ((fp = safe_fopen(file, mode, &omode)) != NULL) { register_file(fp, omode, 0, 0, FP_UNCOMPRESSED, NULL, 0L); fcntl(fileno(fp), F_SETFD, FD_CLOEXEC); } return fp; } FILE * Fdopen(int fd, const char *mode) { FILE *fp; int omode; scan_mode(mode, &omode); if ((fp = fdopen(fd, mode)) != NULL) { register_file(fp, omode, 0, 0, FP_UNCOMPRESSED, NULL, 0L); fcntl(fileno(fp), F_SETFD, FD_CLOEXEC); } return fp; } int Fclose(FILE *fp) { int i = 0; if (unregister_file(fp) == OKAY) i |= 1; if (fclose(fp) == 0) i |= 2; return i == 3 ? 0 : EOF; } FILE * Zopen(const char *file, const char *mode, int *compression) { int input; FILE *output; const char *rp; int omode; char *tempfn; int bits; int _compression; long offset; char *extension; enum protocol p; if (scan_mode(mode, &omode) < 0) return NULL; if (compression == NULL) compression = &_compression; bits = R_OK | (omode == O_RDONLY ? 0 : W_OK); if (omode & O_APPEND && ((p = which_protocol(file)) == PROTO_IMAP || p == PROTO_MAILDIR)) { *compression = p == PROTO_IMAP ? FP_IMAP : FP_MAILDIR; omode = O_RDWR | O_APPEND | O_CREAT; rp = file; input = -1; goto open; } if ((extension = strrchr(file, '.')) != NULL) { rp = file; if (strcmp(extension, ".gz") == 0) goto gzip; if (strcmp(extension, ".bz2") == 0) goto bz2; } if (access(file, F_OK) == 0) { *compression = FP_UNCOMPRESSED; return Fopen(file, mode); } else if (access(rp=savecat(file, ".gz"), bits) == 0) { gzip: *compression = FP_GZIPPED; } else if (access(rp=savecat(file, ".bz2"), bits) == 0) { bz2: *compression = FP_BZIP2ED; } else { *compression = FP_UNCOMPRESSED; return Fopen(file, mode); } if (access(rp, W_OK) < 0) *compression |= FP_READONLY; if ((input = open(rp, bits & W_OK ? O_RDWR : O_RDONLY)) < 0 && ((omode&O_CREAT) == 0 || errno != ENOENT)) return NULL; open: if ((output = Ftemp(&tempfn, "Rz", "w+", 0600, 0)) == NULL) { perror(catgets(catd, CATSET, 167, "tmpfile")); close(input); return NULL; } unlink(tempfn); if (input >= 0 || (*compression&FP_MASK) == FP_IMAP || (*compression&FP_MASK) == FP_MAILDIR) { if (decompress(*compression, input, fileno(output)) < 0) { close(input); Fclose(output); return NULL; } } else { if ((input = creat(rp, 0666)) < 0) { Fclose(output); return NULL; } } close(input); fflush(output); if (omode & O_APPEND) { int flags; if ((flags = fcntl(fileno(output), F_GETFL)) != -1) fcntl(fileno(output), F_SETFL, flags | O_APPEND); offset = ftell(output); } else { rewind(output); offset = 0; } register_file(output, omode, 0, 0, *compression, rp, offset); return output; } FILE * Popen(const char *cmd, const char *mode, const char *shell, int newfd1) { int p[2]; int myside, hisside, fd0, fd1; int pid; char mod[2] = { '0', '\0' }; sigset_t nset; FILE *fp; if (pipe(p) < 0) return NULL; fcntl(p[READ], F_SETFD, FD_CLOEXEC); fcntl(p[WRITE], F_SETFD, FD_CLOEXEC); if (*mode == 'r') { myside = p[READ]; fd0 = -1; hisside = fd1 = p[WRITE]; mod[0] = *mode; } else if (*mode == 'W') { myside = p[WRITE]; hisside = fd0 = p[READ]; fd1 = newfd1; mod[0] = 'w'; } else { myside = p[WRITE]; hisside = fd0 = p[READ]; fd1 = -1; mod[0] = 'w'; } sigemptyset(&nset); if (shell == NULL) { pid = start_command(cmd, &nset, fd0, fd1, NULL, NULL, NULL); } else { pid = start_command(shell, &nset, fd0, fd1, "-c", cmd, NULL); } if (pid < 0) { close(p[READ]); close(p[WRITE]); return NULL; } close(hisside); if ((fp = fdopen(myside, mod)) != NULL) register_file(fp, 0, 1, pid, FP_UNCOMPRESSED, NULL, 0L); return fp; } int Pclose(FILE *ptr) { int i; sigset_t nset, oset; i = file_pid(ptr); if (i < 0) return 0; unregister_file(ptr); fclose(ptr); sigemptyset(&nset); sigaddset(&nset, SIGINT); sigaddset(&nset, SIGHUP); sigprocmask(SIG_BLOCK, &nset, &oset); i = wait_child(i); sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL); return i; } void close_all_files(void) { while (fp_head) if (fp_head->pipe) Pclose(fp_head->fp); else Fclose(fp_head->fp); } static void register_file(FILE *fp, int omode, int pipe, int pid, int compressed, const char *realfile, long offset) { struct fp *fpp; fpp = (struct fp*)smalloc(sizeof *fpp); fpp->fp = fp; fpp->omode = omode; fpp->pipe = pipe; fpp->pid = pid; fpp->link = fp_head; fpp->compressed = compressed; fpp->realfile = realfile ? sstrdup(realfile) : NULL; fpp->offset = offset; fp_head = fpp; } static enum okay compress(struct fp *fpp) { int output; char *command[2]; enum okay ok; if (fpp->omode == O_RDONLY) return OKAY; fflush(fpp->fp); clearerr(fpp->fp); fseek(fpp->fp, fpp->offset, SEEK_SET); if ((fpp->compressed&FP_MASK) == FP_IMAP) { return imap_append(fpp->realfile, fpp->fp); } if ((fpp->compressed&FP_MASK) == FP_MAILDIR) { return maildir_append(fpp->realfile, fpp->fp); } if ((output = open(fpp->realfile, (fpp->omode|O_CREAT)&~O_EXCL, 0666)) < 0) { fprintf(stderr, "Fatal: cannot create "); perror(fpp->realfile); return STOP; } if ((fpp->omode & O_APPEND) == 0) ftruncate(output, 0); switch (fpp->compressed & FP_MASK) { case FP_GZIPPED: command[0] = "gzip"; command[1] = "-c"; break; case FP_BZIP2ED: command[0] = "bzip2"; command[1] = "-c"; break; default: command[0] = "cat"; command[1] = NULL; break; } if (run_command(command[0], 0, fileno(fpp->fp), output, command[1], NULL, NULL) < 0) ok = STOP; else ok = OKAY; close(output); return ok; } static int decompress(int compression, int input, int output) { char *command[2]; /* * Note that it is not possible to handle 'pack' or 'compress' * formats because appending data does not work with them. */ switch (compression & FP_MASK) { case FP_GZIPPED: command[0] = "gzip"; command[1] = "-cd"; break; case FP_BZIP2ED: command[0] = "bzip2"; command[1] = "-cd"; break; case FP_IMAP: return 0; case FP_MAILDIR: return 0; default: command[0] = "cat"; command[1] = NULL; } return run_command(command[0], 0, input, output, command[1], NULL, NULL); } static enum okay unregister_file(FILE *fp) { struct fp **pp, *p; enum okay ok = OKAY; for (pp = &fp_head; (p = *pp) != (struct fp *)NULL; pp = &p->link) if (p->fp == fp) { if ((p->compressed&FP_MASK) != FP_UNCOMPRESSED) ok = compress(p); *pp = p->link; free(p); return ok; } panic(catgets(catd, CATSET, 153, "Invalid file pointer")); /*NOTREACHED*/ return STOP; } static int file_pid(FILE *fp) { struct fp *p; for (p = fp_head; p; p = p->link) if (p->fp == fp) return (p->pid); return -1; } /* * Run a command without a shell, with optional arguments and splicing * of stdin and stdout. The command name can be a sequence of words. * Signals must be handled by the caller. * "Mask" contains the signals to ignore in the new process. * SIGINT is enabled unless it's in the mask. */ /*VARARGS4*/ int run_command(char *cmd, sigset_t *mask, int infd, int outfd, char *a0, char *a1, char *a2) { int pid; if ((pid = start_command(cmd, mask, infd, outfd, a0, a1, a2)) < 0) return -1; return wait_command(pid); } /*VARARGS4*/ int start_command(const char *cmd, sigset_t *mask, int infd, int outfd, const char *a0, const char *a1, const char *a2) { int pid; if ((pid = fork()) < 0) { perror("fork"); return -1; } if (pid == 0) { char *argv[100]; int i = getrawlist(cmd, strlen(cmd), argv, sizeof argv / sizeof *argv, 0); if ((argv[i++] = (char *)a0) != NULL && (argv[i++] = (char *)a1) != NULL && (argv[i++] = (char *)a2) != NULL) argv[i] = NULL; prepare_child(mask, infd, outfd); execvp(argv[0], argv); perror(argv[0]); _exit(1); } return pid; } void prepare_child(sigset_t *nset, int infd, int outfd) { int i; sigset_t fset; /* * All file descriptors other than 0, 1, and 2 are supposed to be * close-on-exec. */ if (infd >= 0) dup2(infd, 0); if (outfd >= 0) dup2(outfd, 1); if (nset) { for (i = 1; i < NSIG; i++) if (sigismember(nset, i)) safe_signal(i, SIG_IGN); if (!sigismember(nset, SIGINT)) safe_signal(SIGINT, SIG_DFL); } sigfillset(&fset); sigprocmask(SIG_UNBLOCK, &fset, (sigset_t *)NULL); } static int wait_command(int pid) { if (wait_child(pid) < 0 && (value("bsdcompat") || value("bsdmsgs"))) { printf(catgets(catd, CATSET, 154, "Fatal error in process.\n")); return -1; } return 0; } static struct child * findchild(int pid) { struct child **cpp; for (cpp = &child; *cpp != (struct child *)NULL && (*cpp)->pid != pid; cpp = &(*cpp)->link) ; if (*cpp == (struct child *)NULL) { *cpp = (struct child *) smalloc(sizeof (struct child)); (*cpp)->pid = pid; (*cpp)->done = (*cpp)->free = 0; (*cpp)->link = (struct child *)NULL; } return *cpp; } static void delchild(struct child *cp) { struct child **cpp; for (cpp = &child; *cpp != cp; cpp = &(*cpp)->link) ; *cpp = cp->link; free(cp); } /*ARGSUSED*/ void sigchild(int signo) { int pid; int status; struct child *cp; again: while ((pid = waitpid(-1, (int*)&status, WNOHANG)) > 0) { cp = findchild(pid); if (cp->free) delchild(cp); else { cp->done = 1; cp->status = status; } } if (pid == -1 && errno == EINTR) goto again; } int wait_status; /* * Mark a child as don't care. */ void free_child(int pid) { sigset_t nset, oset; struct child *cp = findchild(pid); sigemptyset(&nset); sigaddset(&nset, SIGCHLD); sigprocmask(SIG_BLOCK, &nset, &oset); if (cp->done) delchild(cp); else cp->free = 1; sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL); } /* * Wait for a specific child to die. */ #if 0 /* * This version is correct code, but causes harm on some loosing * systems. So we use the second one instead. */ int wait_child(int pid) { sigset_t nset, oset; struct child *cp = findchild(pid); sigemptyset(&nset); sigaddset(&nset, SIGCHLD); sigprocmask(SIG_BLOCK, &nset, &oset); while (!cp->done) sigsuspend(&oset); wait_status = cp->status; delchild(cp); sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL); if (WIFEXITED(wait_status) && (WEXITSTATUS(wait_status) == 0)) return 0; return -1; } #endif int wait_child(int pid) { pid_t term; struct child *cp; struct sigaction nact, oact; nact.sa_handler = SIG_DFL; sigemptyset(&nact.sa_mask); nact.sa_flags = SA_NOCLDSTOP; sigaction(SIGCHLD, &nact, &oact); cp = findchild(pid); if (!cp->done) { do { term = wait(&wait_status); if (term == -1 && errno == EINTR) continue; if (term == 0 || term == -1) break; cp = findchild(term); if (cp->free || term == pid) { delchild(cp); } else { cp->done = 1; cp->status = wait_status; } } while (term != pid); } else { wait_status = cp->status; delchild(cp); } sigaction(SIGCHLD, &oact, NULL); /* * Make sure no zombies are left. */ sigchild(SIGCHLD); if (WIFEXITED(wait_status) && (WEXITSTATUS(wait_status) == 0)) return 0; return -1; } heirloom-mailx-12.5/quit.c000066400000000000000000000344051155563371200155330ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)quit.c 2.30 (gritter) 11/11/08"; #endif #endif /* not lint */ #include "rcv.h" #include "extern.h" #include #include #include #include #include #include /* * Rcv -- receive mail rationally. * * Termination processing. */ static int writeback(FILE *res, FILE *obuf); static void edstop(void); /* * The "quit" command. */ /*ARGSUSED*/ int quitcmd(void *v) { /* * If we are sourcing, then return 1 so execute() can handle it. * Otherwise, return -1 to abort command loop. */ if (sourcing) return 1; return -1; } /* * Preserve all the appropriate messages back in the system * mailbox, and print a nice message indicated how many were * saved. On any error, just return -1. Else return 0. * Incorporate the any new mail that we found. */ static int writeback(FILE *res, FILE *obuf) { struct message *mp; int p, c; p = 0; fseek(obuf, 0L, SEEK_SET); #ifndef APPEND if (res != NULL) while ((c = getc(res)) != EOF) putc(c, obuf); #endif for (mp = &message[0]; mp < &message[msgCount]; mp++) if ((mp->m_flag&MPRESERVE)||(mp->m_flag&MTOUCH)==0) { p++; if (send(mp, obuf, NULL, NULL, SEND_MBOX, NULL) < 0) { perror(mailname); fseek(obuf, 0L, SEEK_SET); return(-1); } } #ifdef APPEND if (res != NULL) while ((c = getc(res)) != EOF) putc(c, obuf); #endif fflush(obuf); trunc(obuf); if (ferror(obuf)) { perror(mailname); fseek(obuf, 0L, SEEK_SET); return(-1); } if (res != NULL) Fclose(res); fseek(obuf, 0L, SEEK_SET); alter(mailname); if (p == 1) printf(catgets(catd, CATSET, 155, "Held 1 message in %s\n"), mailname); else printf(catgets(catd, CATSET, 156, "Held %d messages in %s\n"), p, mailname); return(0); } /* * Save all of the undetermined messages at the top of "mbox" * Save all untouched messages back in the system mailbox. * Remove the system mailbox, if none saved there. */ void quit(void) { int p, modify, anystat; FILE *fbuf, *rbuf, *readstat = NULL, *abuf; struct message *mp; int c; char *tempResid; struct stat minfo; /* * If we are read only, we can't do anything, * so just return quickly. IMAP can set some * flags (e.g. "\\Seen") so imap_quit must be * called even then. */ if (mb.mb_perm == 0 && mb.mb_type != MB_IMAP) return; switch (mb.mb_type) { case MB_FILE: break; case MB_MAILDIR: maildir_quit(); return; case MB_POP3: pop3_quit(); return; case MB_IMAP: case MB_CACHE: imap_quit(); return; case MB_VOID: return; } /* * If editing (not reading system mail box), then do the work * in edstop() */ if (edit) { edstop(); return; } /* * See if there any messages to save in mbox. If no, we * can save copying mbox to /tmp and back. * * Check also to see if any files need to be preserved. * Delete all untouched messages to keep them out of mbox. * If all the messages are to be preserved, just exit with * a message. */ fbuf = Zopen(mailname, "r+", &mb.mb_compressed); if (fbuf == NULL) { if (errno == ENOENT) return; goto newmail; } if (fcntl_lock(fileno(fbuf), F_WRLCK) == -1) { nolock: perror(catgets(catd, CATSET, 157, "Unable to lock mailbox")); Fclose(fbuf); return; } if (dot_lock(mailname, fileno(fbuf), 1, stdout, ".") == -1) goto nolock; rbuf = NULL; if (fstat(fileno(fbuf), &minfo) >= 0 && minfo.st_size > mailsize) { printf(catgets(catd, CATSET, 158, "New mail has arrived.\n")); rbuf = Ftemp(&tempResid, "Rq", "w", 0600, 1); if (rbuf == NULL || fbuf == NULL) goto newmail; #ifdef APPEND fseek(fbuf, (long)mailsize, SEEK_SET); while ((c = getc(fbuf)) != EOF) putc(c, rbuf); #else p = minfo.st_size - mailsize; while (p-- > 0) { c = getc(fbuf); if (c == EOF) goto newmail; putc(c, rbuf); } #endif Fclose(rbuf); if ((rbuf = Fopen(tempResid, "r")) == NULL) goto newmail; rm(tempResid); Ftfree(&tempResid); } anystat = holdbits(); modify = 0; if (Tflag != NULL) { if ((readstat = Zopen(Tflag, "w", NULL)) == NULL) Tflag = NULL; } for (c = 0, p = 0, mp = &message[0]; mp < &message[msgCount]; mp++) { if (mp->m_flag & MBOX) c++; if (mp->m_flag & MPRESERVE) p++; if (mp->m_flag & MODIFY) modify++; if (readstat != NULL && (mp->m_flag & (MREAD|MDELETED)) != 0) { char *id; if ((id = hfield("message-id", mp)) != NULL || (id = hfield("article-id", mp)) != NULL) fprintf(readstat, "%s\n", id); } } if (readstat != NULL) Fclose(readstat); if (p == msgCount && !modify && !anystat) { if (p == 1) printf(catgets(catd, CATSET, 155, "Held 1 message in %s\n"), mailname); else if (p > 1) printf(catgets(catd, CATSET, 156, "Held %d messages in %s\n"), p, mailname); Fclose(fbuf); dot_unlock(mailname); return; } if (c == 0) { if (p != 0) { writeback(rbuf, fbuf); Fclose(fbuf); dot_unlock(mailname); return; } goto cream; } if (makembox() == STOP) { Fclose(fbuf); dot_unlock(mailname); return; } /* * Now we are ready to copy back preserved files to * the system mailbox, if any were requested. */ if (p != 0) { writeback(rbuf, fbuf); Fclose(fbuf); dot_unlock(mailname); return; } /* * Finally, remove his /usr/mail file. * If new mail has arrived, copy it back. */ cream: if (rbuf != NULL) { abuf = fbuf; fseek(abuf, 0L, SEEK_SET); while ((c = getc(rbuf)) != EOF) putc(c, abuf); Fclose(rbuf); trunc(abuf); alter(mailname); Fclose(fbuf); dot_unlock(mailname); return; } demail(); Fclose(fbuf); dot_unlock(mailname); return; newmail: printf(catgets(catd, CATSET, 166, "Thou hast new mail.\n")); if (fbuf != NULL) { Fclose(fbuf); dot_unlock(mailname); } } /* * Adjust the message flags in each message. */ int holdbits(void) { struct message *mp; int anystat, autohold, holdbit, nohold; anystat = 0; autohold = value("hold") != NULL; holdbit = autohold ? MPRESERVE : MBOX; nohold = MBOX|MSAVED|MDELETED|MPRESERVE; if (value("keepsave") != NULL) nohold &= ~MSAVED; for (mp = &message[0]; mp < &message[msgCount]; mp++) { if (mp->m_flag & MNEW) { mp->m_flag &= ~MNEW; mp->m_flag |= MSTATUS; } if (mp->m_flag & (MSTATUS|MFLAG|MUNFLAG|MANSWER|MUNANSWER| MDRAFT|MUNDRAFT)) anystat++; if ((mp->m_flag & MTOUCH) == 0) mp->m_flag |= MPRESERVE; if ((mp->m_flag & nohold) == 0) mp->m_flag |= holdbit; } return anystat; } /* * Create another temporary file and copy user's mbox file * darin. If there is no mbox, copy nothing. * If he has specified "append" don't copy his mailbox, * just copy saveable entries at the end. */ enum okay makembox(void) { struct message *mp; char *mbox, *tempQuit; int mcount, c; FILE *ibuf = NULL, *obuf, *abuf; enum protocol prot; mbox = mboxname; mcount = 0; if (value("append") == NULL) { if ((obuf = Ftemp(&tempQuit, "Rm", "w", 0600, 1)) == NULL) { perror(catgets(catd, CATSET, 162, "temporary mail quit file")); return STOP; } if ((ibuf = Fopen(tempQuit, "r")) == NULL) { perror(tempQuit); rm(tempQuit); Ftfree(&tempQuit); Fclose(obuf); return STOP; } rm(tempQuit); Ftfree(&tempQuit); if ((abuf = Zopen(mbox, "r", NULL)) != NULL) { while ((c = getc(abuf)) != EOF) putc(c, obuf); Fclose(abuf); } if (ferror(obuf)) { perror(catgets(catd, CATSET, 163, "temporary mail quit file")); Fclose(ibuf); Fclose(obuf); return STOP; } Fclose(obuf); close(creat(mbox, 0600)); if ((obuf = Zopen(mbox, "r+", NULL)) == NULL) { perror(mbox); Fclose(ibuf); return STOP; } } else { if ((obuf = Zopen(mbox, "a", NULL)) == NULL) { perror(mbox); return STOP; } fchmod(fileno(obuf), 0600); } prot = which_protocol(mbox); for (mp = &message[0]; mp < &message[msgCount]; mp++) if (mp->m_flag & MBOX) { mcount++; if (prot == PROTO_IMAP && saveignore[0].i_count == 0 && saveignore[1].i_count == 0 && imap_thisaccount(mbox)) { if (imap_copy(mp, mp-message+1, mbox) == STOP) goto err; } else if (send(mp, obuf, saveignore, NULL, SEND_MBOX, NULL) < 0) { perror(mbox); err: if (ibuf) Fclose(ibuf); Fclose(obuf); return STOP; } mp->m_flag |= MBOXED; } /* * Copy the user's old mbox contents back * to the end of the stuff we just saved. * If we are appending, this is unnecessary. */ if (value("append") == NULL) { rewind(ibuf); c = getc(ibuf); while (c != EOF) { putc(c, obuf); if (ferror(obuf)) break; c = getc(ibuf); } Fclose(ibuf); fflush(obuf); } trunc(obuf); if (ferror(obuf)) { perror(mbox); Fclose(obuf); return STOP; } if (Fclose(obuf) != 0) { if (prot != PROTO_IMAP) perror(mbox); return STOP; } if (mcount == 1) printf(catgets(catd, CATSET, 164, "Saved 1 message in mbox\n")); else printf(catgets(catd, CATSET, 165, "Saved %d messages in mbox\n"), mcount); return OKAY; } /* * Terminate an editing session by attempting to write out the user's * file from the temporary. Save any new stuff appended to the file. */ static void edstop(void) { int gotcha, c; struct message *mp; FILE *obuf, *ibuf = NULL, *readstat = NULL; struct stat statb; if (mb.mb_perm == 0) return; holdsigs(); if (Tflag != NULL) { if ((readstat = Zopen(Tflag, "w", NULL)) == NULL) Tflag = NULL; } for (mp = &message[0], gotcha = 0; mp < &message[msgCount]; mp++) { if (mp->m_flag & MNEW) { mp->m_flag &= ~MNEW; mp->m_flag |= MSTATUS; } if (mp->m_flag & (MODIFY|MDELETED|MSTATUS|MFLAG|MUNFLAG| MANSWER|MUNANSWER|MDRAFT|MUNDRAFT)) gotcha++; if (readstat != NULL && (mp->m_flag & (MREAD|MDELETED)) != 0) { char *id; if ((id = hfield("message-id", mp)) != NULL || (id = hfield("article-id", mp)) != NULL) fprintf(readstat, "%s\n", id); } } if (readstat != NULL) Fclose(readstat); if (!gotcha || Tflag != NULL) goto done; ibuf = NULL; if (stat(mailname, &statb) >= 0 && statb.st_size > mailsize) { char *tempname; if ((obuf = Ftemp(&tempname, "mbox.", "w", 0600, 1)) == NULL) { perror(catgets(catd, CATSET, 167, "tmpfile")); relsesigs(); reset(0); } if ((ibuf = Zopen(mailname, "r", &mb.mb_compressed)) == NULL) { perror(mailname); Fclose(obuf); rm(tempname); Ftfree(&tempname); relsesigs(); reset(0); } fseek(ibuf, (long)mailsize, SEEK_SET); while ((c = getc(ibuf)) != EOF) putc(c, obuf); Fclose(ibuf); Fclose(obuf); if ((ibuf = Fopen(tempname, "r")) == NULL) { perror(tempname); rm(tempname); Ftfree(&tempname); relsesigs(); reset(0); } rm(tempname); Ftfree(&tempname); } printf(catgets(catd, CATSET, 168, "\"%s\" "), mailname); fflush(stdout); if ((obuf = Zopen(mailname, "r+", &mb.mb_compressed)) == NULL) { perror(mailname); relsesigs(); reset(0); } trunc(obuf); c = 0; for (mp = &message[0]; mp < &message[msgCount]; mp++) { if ((mp->m_flag & MDELETED) != 0) continue; c++; if (send(mp, obuf, NULL, NULL, SEND_MBOX, NULL) < 0) { perror(mailname); relsesigs(); reset(0); } } gotcha = (c == 0 && ibuf == NULL); if (ibuf != NULL) { while ((c = getc(ibuf)) != EOF) putc(c, obuf); Fclose(ibuf); } fflush(obuf); if (ferror(obuf)) { perror(mailname); relsesigs(); reset(0); } Fclose(obuf); if (gotcha && value("emptybox") == NULL) { rm(mailname); printf(value("bsdcompat") || value("bsdmsgs") ? catgets(catd, CATSET, 169, "removed\n") : catgets(catd, CATSET, 211, "removed.\n")); } else printf(value("bsdcompat") || value("bsdmsgs") ? catgets(catd, CATSET, 170, "complete\n") : catgets(catd, CATSET, 212, "updated.\n")); fflush(stdout); done: relsesigs(); } enum quitflags { QUITFLAG_HOLD = 001, QUITFLAG_KEEPSAVE = 002, QUITFLAG_APPEND = 004, QUITFLAG_EMPTYBOX = 010 }; static const struct quitnames { enum quitflags flag; const char *name; } quitnames[] = { { QUITFLAG_HOLD, "hold" }, { QUITFLAG_KEEPSAVE, "keepsave" }, { QUITFLAG_APPEND, "append" }, { QUITFLAG_EMPTYBOX, "emptybox" }, { 0, NULL } }; int savequitflags(void) { enum quitflags qf = 0; int i; for (i = 0; quitnames[i].name; i++) if (value(quitnames[i].name)) qf |= quitnames[i].flag; return qf; } void restorequitflags(int qf) { int i; for (i = 0; quitnames[i].name; i++) if (qf & quitnames[i].flag) { if (value(quitnames[i].name) == NULL) assign(quitnames[i].name, ""); } else if (value(quitnames[i].name)) unset_internal(quitnames[i].name); } heirloom-mailx-12.5/rcv.h000066400000000000000000000046561155563371200153550ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Sccsid @(#)rcv.h 2.7 (gritter) 3/4/06 */ /* * Mail -- a mail program * * This file is included by normal files which want both * globals and declarations. */ #include "config.h" #include #include #include #include #include #include #ifdef HAVE_ICONV #include #endif /* HAVE_ICONV */ #ifdef HAVE_ALLOCA_H #include #endif /* HAVE_ALLOCA_H */ #define SHELL "/bin/sh" #include "def.h" #include "glob.h" heirloom-mailx-12.5/send.c000066400000000000000000000727601155563371200155100ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)send.c 2.86 (gritter) 2/4/08"; #endif #endif /* not lint */ #include "rcv.h" #include "extern.h" #include #include #include /* * Mail -- a mail program * * Mail to mail folders and displays. */ enum parseflags { PARSE_DEFAULT = 0, PARSE_DECRYPT = 01, PARSE_PARTS = 02 }; static void onpipe(int signo); extern void brokpipe(int signo); static int sendpart(struct message *zmp, struct mimepart *ip, FILE *obuf, struct ignoretab *doign, char *prefix, size_t prefixlen, enum sendaction action, off_t *stats, int level); static struct mimepart *parsemsg(struct message *mp, enum parseflags pf); static enum okay parsepart(struct message *zmp, struct mimepart *ip, enum parseflags pf, int level); static void parsemultipart(struct message *zmp, struct mimepart *ip, enum parseflags pf, int level); static void newpart(struct mimepart *ip, struct mimepart **np, off_t offs, int *part); static void endpart(struct mimepart **np, off_t xoffs, long lines); static void parse822(struct message *zmp, struct mimepart *ip, enum parseflags pf, int level); static void parsepkcs7(struct message *zmp, struct mimepart *ip, enum parseflags pf, int level); static size_t out(char *buf, size_t len, FILE *fp, enum conversion convert, enum sendaction action, char *prefix, size_t prefixlen, off_t *stats, char **restp, size_t *restsizep); static void addstats(off_t *stats, off_t lines, off_t bytes); static FILE *newfile(struct mimepart *ip, int *ispipe, sighandler_type *oldpipe); static char *getpipecmd(char *content); static FILE *getpipefile(char *cmd, FILE **qbuf, int quote); static void pipecpy(FILE *pipebuf, FILE *outbuf, FILE *origobuf, char *prefix, size_t prefixlen, off_t *stats); static void statusput(const struct message *mp, FILE *obuf, char *prefix, off_t *stats); static void xstatusput(const struct message *mp, FILE *obuf, char *prefix, off_t *stats); static void put_from_(FILE *fp, struct mimepart *ip); static sigjmp_buf pipejmp; /*ARGSUSED*/ static void onpipe(int signo) { siglongjmp(pipejmp, 1); } /* * Send message described by the passed pointer to the * passed output buffer. Return -1 on error. * Adjust the status: field if need be. * If doign is given, suppress ignored header fields. * prefix is a string to prepend to each output line. * action = data destination (SEND_MBOX,_TOFILE,_TODISP,_QUOTE,_DECRYPT). * stats[0] is line count, stats[1] is character count. stats may be NULL. * Note that stats[0] is valid for SEND_MBOX only. */ int send(struct message *mp, FILE *obuf, struct ignoretab *doign, char *prefix, enum sendaction action, off_t *stats) { size_t count; FILE *ibuf; size_t prefixlen, sz; int c; enum parseflags pf; struct mimepart *ip; char *cp, *cp2; if (mp == dot && action != SEND_TOSRCH && action != SEND_TOFLTR) did_print_dot = 1; if (stats) stats[0] = stats[1] = 0; /* * Compute the prefix string, without trailing whitespace */ if (prefix != NULL) { cp2 = 0; for (cp = prefix; *cp; cp++) if (!blankchar(*cp & 0377)) cp2 = cp; prefixlen = cp2 == 0 ? 0 : cp2 - prefix + 1; } else prefixlen = 0; /* * First line is the From_ line, so no headers there to worry about. */ if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL) return -1; count = mp->m_size; sz = 0; if (mp->m_flag & MNOFROM) { if (doign != allignore && doign != fwdignore && action != SEND_RFC822) sz = fprintf(obuf, "%sFrom %s %s\n", prefix ? prefix : "", fakefrom(mp), fakedate(mp->m_time)); } else { if (prefix && doign != allignore && doign != fwdignore && action != SEND_RFC822) { fputs(prefix, obuf); sz += strlen(prefix); } while (count && (c = getc(ibuf)) != EOF) { if (doign != allignore && doign != fwdignore && action != SEND_RFC822) { putc(c, obuf); sz++; } count--; if (c == '\n') break; } } if (sz) addstats(stats, 1, sz); pf = 0; if (action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW) pf |= PARSE_DECRYPT|PARSE_PARTS; if ((ip = parsemsg(mp, pf)) == NULL) return -1; return sendpart(mp, ip, obuf, doign, prefix, prefixlen, action, stats, 0); } static int sendpart(struct message *zmp, struct mimepart *ip, FILE *obuf, struct ignoretab *doign, char *prefix, size_t prefixlen, enum sendaction action, off_t *stats, int level) { char *line = NULL; size_t linesize = 0, linelen, count, len; int dostat, infld = 0, ignoring = 1, isenc; char *cp, *cp2, *start; int c; struct mimepart *np; FILE *ibuf = NULL, *pbuf = obuf, *qbuf = obuf, *origobuf = obuf; char *tcs, *pipecmd = NULL; enum conversion convert; sighandler_type oldpipe = SIG_DFL; int rt = 0; long lineno = 0; int ispipe = 0; char *rest; size_t restsize; int eof; (void)&ibuf; (void)&pbuf; (void)&convert; (void)&oldpipe; (void)&rt; (void)&obuf; (void)&stats; (void)&action; if (ip->m_mimecontent == MIME_PKCS7 && ip->m_multipart && action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW) goto skip; dostat = 0; if (level == 0) { if (doign != NULL) { if (!is_ign("status", 6, doign)) dostat |= 1; if (!is_ign("x-status", 8, doign)) dostat |= 2; } else dostat = 3; } if ((ibuf = setinput(&mb, (struct message *)ip, NEED_BODY)) == NULL) return -1; count = ip->m_size; if (ip->m_mimecontent == MIME_DISCARD) goto skip; if ((ip->m_flag&MNOFROM) == 0) while (count && (c = getc(ibuf)) != EOF) { count--; if (c == '\n') break; } isenc = 0; convert = action == SEND_TODISP || action == SEND_TODISP_ALL || action == SEND_QUOTE || action == SEND_QUOTE_ALL || action == SEND_TOSRCH || action == SEND_TOFLTR ? CONV_FROMHDR : CONV_NONE; while (foldergets(&line, &linesize, &count, &linelen, ibuf)) { lineno++; if (line[0] == '\n') { /* * If line is blank, we've reached end of * headers, so force out status: field * and note that we are no longer in header * fields */ if (dostat & 1) statusput(zmp, obuf, prefix, stats); if (dostat & 2) xstatusput(zmp, obuf, prefix, stats); if (doign != allignore) out("\n", 1, obuf, CONV_NONE, SEND_MBOX, prefix, prefixlen, stats, NULL, NULL); break; } isenc &= ~1; if (infld && blankchar(line[0]&0377)) { /* * If this line is a continuation (via space or tab) * of a previous header field, determine if the start * of the line is a MIME encoded word. */ if (isenc & 2) { for (cp = line; blankchar(*cp&0377); cp++); if (cp > line && linelen - (cp - line) > 8 && cp[0] == '=' && cp[1] == '?') isenc |= 1; } } else { /* * Pick up the header field if we have one. */ for (cp = line; (c = *cp&0377) && c != ':' && !spacechar(c); cp++); cp2 = cp; while (spacechar(*cp&0377)) cp++; if (cp[0] != ':' && level == 0 && lineno == 1) { /* * Not a header line, force out status: * This happens in uucp style mail where * there are no headers at all. */ if (dostat & 1) statusput(zmp, obuf, prefix, stats); if (dostat & 2) xstatusput(zmp, obuf, prefix, stats); if (doign != allignore) out("\n", 1, obuf, CONV_NONE, SEND_MBOX, prefix, prefixlen, stats, NULL, NULL); break; } /* * If it is an ignored field and * we care about such things, skip it. */ c = *cp2; *cp2 = 0; /* temporarily null terminate */ if (doign && is_ign(line, cp2 - line, doign)) ignoring = 1; else if (asccasecmp(line, "status") == 0) { /* * If the field is "status," go compute * and print the real Status: field */ if (dostat & 1) { statusput(zmp, obuf, prefix, stats); dostat &= ~1; ignoring = 1; } } else if (asccasecmp(line, "x-status") == 0) { /* * If the field is "status," go compute * and print the real Status: field */ if (dostat & 2) { xstatusput(zmp, obuf, prefix, stats); dostat &= ~2; ignoring = 1; } } else ignoring = 0; *cp2 = c; infld = 1; } /* * Determine if the end of the line is a MIME encoded word. */ isenc &= ~2; if (count && (c = getc(ibuf)) != EOF) { if (blankchar(c)) { if (linelen > 0 && line[linelen-1] == '\n') cp = &line[linelen-2]; else cp = &line[linelen-1]; while (cp >= line && whitechar(*cp&0377)) cp++; if (cp - line > 8 && cp[0] == '=' && cp[-1] == '?') isenc |= 2; } ungetc(c, ibuf); } if (!ignoring) { start = line; len = linelen; if (action == SEND_TODISP || action == SEND_TODISP_ALL || action == SEND_QUOTE || action == SEND_QUOTE_ALL || action == SEND_TOSRCH || action == SEND_TOFLTR) { /* * Strip blank characters if two MIME-encoded * words follow on continuing lines. */ if (isenc & 1) while (len>0&&blankchar(*start&0377)) { start++; len--; } if (isenc & 2) if (len > 0 && start[len-1] == '\n') len--; while (len > 0 && blankchar(start[len-1]&0377)) len--; } out(start, len, obuf, convert, action, prefix, prefixlen, stats, NULL, NULL); if (ferror(obuf)) { free(line); return -1; } } } free(line); line = NULL; skip: switch (ip->m_mimecontent) { case MIME_822: switch (action) { case SEND_TOFLTR: putc('\0', obuf); /*FALLTHRU*/ case SEND_TODISP: case SEND_TODISP_ALL: case SEND_QUOTE: case SEND_QUOTE_ALL: put_from_(obuf, ip->m_multipart); /*FALLTHRU*/ case SEND_TOSRCH: case SEND_DECRYPT: goto multi; case SEND_TOFILE: case SEND_TOPIPE: put_from_(obuf, ip->m_multipart); /*FALLTHRU*/ case SEND_MBOX: case SEND_RFC822: case SEND_SHOW: break; } break; case MIME_TEXT_HTML: if (action == SEND_TOFLTR) putc('\b', obuf); /*FALLTHRU*/ case MIME_TEXT: case MIME_TEXT_PLAIN: switch (action) { case SEND_TODISP: case SEND_TODISP_ALL: case SEND_QUOTE: case SEND_QUOTE_ALL: pipecmd = getpipecmd(ip->m_ct_type_plain); /*FALLTHRU*/ default: break; } break; case MIME_DISCARD: if (action != SEND_DECRYPT) return rt; break; case MIME_PKCS7: if (action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW && ip->m_multipart) goto multi; /*FALLTHRU*/ default: switch (action) { case SEND_TODISP: case SEND_TODISP_ALL: case SEND_QUOTE: case SEND_QUOTE_ALL: if ((pipecmd = getpipecmd(ip->m_ct_type_plain)) != NULL) break; if (level == 0 && count) { cp = "[Binary content]\n\n"; out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, prefix, prefixlen, stats, NULL, NULL); } /*FALLTHRU*/ case SEND_TOFLTR: return rt; case SEND_TOFILE: case SEND_TOPIPE: case SEND_TOSRCH: case SEND_DECRYPT: case SEND_MBOX: case SEND_RFC822: case SEND_SHOW: break; } break; case MIME_ALTERNATIVE: if ((action == SEND_TODISP || action == SEND_QUOTE) && value("print-alternatives") == NULL) for (np = ip->m_multipart; np; np = np->m_nextpart) if (np->m_mimecontent == MIME_TEXT_PLAIN) { if (sendpart(zmp, np, obuf, doign, prefix, prefixlen, action, stats, level+1) < 0) return -1; return rt; } /*FALLTHRU*/ case MIME_MULTI: case MIME_DIGEST: switch (action) { case SEND_TODISP: case SEND_TODISP_ALL: case SEND_QUOTE: case SEND_QUOTE_ALL: case SEND_TOFILE: case SEND_TOPIPE: case SEND_TOSRCH: case SEND_TOFLTR: case SEND_DECRYPT: multi: if ((action == SEND_TODISP || action == SEND_TODISP_ALL) && ip->m_multipart != NULL && ip->m_multipart->m_mimecontent == MIME_DISCARD && ip->m_multipart->m_nextpart == NULL) { cp = "[Missing multipart boundary - " "use \"show\" to display " "the raw message]\n\n"; out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, prefix, prefixlen, stats, NULL, NULL); } for (np = ip->m_multipart; np; np = np->m_nextpart) { if (np->m_mimecontent == MIME_DISCARD && action != SEND_DECRYPT) continue; switch (action) { case SEND_TOFILE: if (np->m_partstring && strcmp(np->m_partstring, "1") == 0) break; stats = NULL; if ((obuf = newfile(np, &ispipe, &oldpipe)) == NULL) continue; break; case SEND_TODISP: case SEND_TODISP_ALL: case SEND_QUOTE_ALL: if ((ip->m_mimecontent == MIME_MULTI || ip->m_mimecontent == MIME_ALTERNATIVE || ip->m_mimecontent == MIME_DIGEST) && np->m_partstring) { len = strlen(np->m_partstring) + 40; cp = ac_alloc(len); snprintf(cp, len, "%sPart %s:\n", level || strcmp(np->m_partstring, "1") ? "\n" : "", np->m_partstring); out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, prefix, prefixlen, stats, NULL, NULL); ac_free(cp); } break; case SEND_TOFLTR: putc('\0', obuf); /*FALLTHRU*/ case SEND_MBOX: case SEND_RFC822: case SEND_SHOW: case SEND_TOSRCH: case SEND_QUOTE: case SEND_DECRYPT: case SEND_TOPIPE: break; } if (sendpart(zmp, np, obuf, doign, prefix, prefixlen, action, stats, level+1) < 0) rt = -1; else if (action == SEND_QUOTE) break; if (action == SEND_TOFILE && obuf != origobuf) { if (ispipe == 0) Fclose(obuf); else { safe_signal(SIGPIPE, SIG_IGN); Pclose(obuf); safe_signal(SIGPIPE, oldpipe); } } } return rt; case SEND_MBOX: case SEND_RFC822: case SEND_SHOW: break; } } /* * Copy out message body */ if (doign == allignore && level == 0) /* skip final blank line */ count--; switch (ip->m_mimeenc) { case MIME_BIN: if (stats) stats[0] = -1; /*FALLTHRU*/ case MIME_7B: case MIME_8B: convert = CONV_NONE; break; case MIME_QP: convert = CONV_FROMQP; break; case MIME_B64: switch (ip->m_mimecontent) { case MIME_TEXT: case MIME_TEXT_PLAIN: case MIME_TEXT_HTML: convert = CONV_FROMB64_T; break; default: convert = CONV_FROMB64; } break; default: convert = CONV_NONE; } if (action == SEND_DECRYPT || action == SEND_MBOX || action == SEND_RFC822 || action == SEND_SHOW) convert = CONV_NONE; tcs = gettcharset(); #ifdef HAVE_ICONV if ((action == SEND_TODISP || action == SEND_TODISP_ALL || action == SEND_QUOTE || action == SEND_QUOTE_ALL || action == SEND_TOSRCH) && (ip->m_mimecontent == MIME_TEXT_PLAIN || ip->m_mimecontent == MIME_TEXT_HTML || ip->m_mimecontent == MIME_TEXT)) { if (iconvd != (iconv_t)-1) iconv_close(iconvd); if (asccasecmp(tcs, ip->m_charset) && asccasecmp(us_ascii, ip->m_charset)) iconvd = iconv_open_ft(tcs, ip->m_charset); else iconvd = (iconv_t)-1; } #endif /* HAVE_ICONV */ if ((action == SEND_TODISP || action == SEND_TODISP_ALL || action == SEND_QUOTE || action == SEND_QUOTE_ALL) && pipecmd != NULL) { qbuf = obuf; pbuf = getpipefile(pipecmd, &qbuf, action == SEND_QUOTE || action == SEND_QUOTE_ALL); action = SEND_TOPIPE; if (pbuf != qbuf) { oldpipe = safe_signal(SIGPIPE, onpipe); if (sigsetjmp(pipejmp, 1)) goto end; } } else pbuf = qbuf = obuf; eof = 0; while (!eof && foldergets(&line, &linesize, &count, &linelen, ibuf)) { lineno++; while (convert == CONV_FROMQP && linelen >= 2 && line[linelen-2] == '=') { char *line2; size_t linesize2, linelen2; nextl: line2 = NULL; linesize2 = 0; if (foldergets(&line2, &linesize2, &count, &linelen2, ibuf) == NULL) { eof = 1; break; } if (linelen + linelen2 + 1 > linesize) line = srealloc(line, linesize = linelen + linelen2 + 1); memcpy(&line[linelen], line2, linelen2+1); linelen += linelen2; free(line2); } rest = NULL; restsize = 0; out(line, linelen, pbuf, convert, action, pbuf == origobuf ? prefix : NULL, pbuf == origobuf ? prefixlen : 0, pbuf == origobuf ? stats : NULL, eof ? NULL : &rest, eof ? NULL : &restsize); if (ferror(pbuf)) { rt = -1; break; } if (restsize) { if (line != rest) memmove(line, rest, restsize); linelen = restsize; goto nextl; } } end: free(line); if (pbuf != qbuf) { safe_signal(SIGPIPE, SIG_IGN); Pclose(pbuf); safe_signal(SIGPIPE, oldpipe); if (qbuf != obuf) pipecpy(qbuf, obuf, origobuf, prefix, prefixlen, stats); } #ifdef HAVE_ICONV if (iconvd != (iconv_t)-1) { iconv_close(iconvd); iconvd = (iconv_t)-1; } #endif return rt; } static struct mimepart * parsemsg(struct message *mp, enum parseflags pf) { struct mimepart *ip; ip = csalloc(1, sizeof *ip); ip->m_flag = mp->m_flag; ip->m_have = mp->m_have; ip->m_block = mp->m_block; ip->m_offset = mp->m_offset; ip->m_size = mp->m_size; ip->m_xsize = mp->m_xsize; ip->m_lines = mp->m_lines; ip->m_xlines = mp->m_lines; if (parsepart(mp, ip, pf, 0) != OKAY) return NULL; return ip; } static enum okay parsepart(struct message *zmp, struct mimepart *ip, enum parseflags pf, int level) { char *cp; ip->m_ct_type = hfield("content-type", (struct message *)ip); if (ip->m_ct_type != NULL) { ip->m_ct_type_plain = savestr(ip->m_ct_type); if ((cp = strchr(ip->m_ct_type_plain, ';')) != NULL) *cp = '\0'; } else if (ip->m_parent && ip->m_parent->m_mimecontent == MIME_DIGEST) ip->m_ct_type_plain = "message/rfc822"; else ip->m_ct_type_plain = "text/plain"; ip->m_mimecontent = mime_getcontent(ip->m_ct_type_plain); if (ip->m_ct_type) ip->m_charset = mime_getparam("charset", ip->m_ct_type); if (ip->m_charset == NULL) ip->m_charset = us_ascii; ip->m_ct_transfer_enc = hfield("content-transfer-encoding", (struct message *)ip); ip->m_mimeenc = ip->m_ct_transfer_enc ? mime_getenc(ip->m_ct_transfer_enc) : MIME_7B; if ((cp = hfield("content-disposition", (struct message *)ip)) == 0 || (ip->m_filename = mime_getparam("filename", cp)) == 0) if (ip->m_ct_type != NULL) ip->m_filename = mime_getparam("name", ip->m_ct_type); if (pf & PARSE_PARTS) { if (level > 9999) { fprintf(stderr, "MIME content too deeply nested.\n"); return STOP; } switch (ip->m_mimecontent) { case MIME_PKCS7: if (pf & PARSE_DECRYPT) { parsepkcs7(zmp, ip, pf, level); break; } /*FALLTHRU*/ default: break; case MIME_MULTI: case MIME_ALTERNATIVE: case MIME_DIGEST: parsemultipart(zmp, ip, pf, level); break; case MIME_822: parse822(zmp, ip, pf, level); break; } } return OKAY; } static void parsemultipart(struct message *zmp, struct mimepart *ip, enum parseflags pf, int level) { char *boundary; char *line = NULL; size_t linesize = 0, linelen, count, boundlen; FILE *ibuf; struct mimepart *np = NULL; off_t offs; int part = 0; long lines = 0; if ((boundary = mime_getboundary(ip->m_ct_type)) == NULL) return; boundlen = strlen(boundary); if ((ibuf = setinput(&mb, (struct message *)ip, NEED_BODY)) == NULL) return; count = ip->m_size; while (foldergets(&line, &linesize, &count, &linelen, ibuf)) if (line[0] == '\n') break; offs = ftell(ibuf); newpart(ip, &np, offs, NULL); while (foldergets(&line, &linesize, &count, &linelen, ibuf)) { if ((lines > 0 || part == 0) && linelen >= boundlen + 1 && strncmp(line, boundary, boundlen) == 0) { if (line[boundlen] == '\n') { offs = ftell(ibuf); if (part != 0) { endpart(&np, offs-boundlen-2, lines); newpart(ip, &np, offs-boundlen-2, NULL); } endpart(&np, offs, 2); newpart(ip, &np, offs, &part); lines = 0; } else if (line[boundlen] == '-' && line[boundlen+1] == '-' && line[boundlen+2] == '\n') { offs = ftell(ibuf); if (part != 0) { endpart(&np, offs-boundlen-4, lines); newpart(ip, &np, offs-boundlen-4, NULL); } endpart(&np, offs+count, 2); break; } else lines++; } else lines++; } if (np) { offs = ftell(ibuf); endpart(&np, offs, lines); } for (np = ip->m_multipart; np; np = np->m_nextpart) if (np->m_mimecontent != MIME_DISCARD) parsepart(zmp, np, pf, level+1); free(line); } static void newpart(struct mimepart *ip, struct mimepart **np, off_t offs, int *part) { struct mimepart *pp; size_t sz; *np = csalloc(1, sizeof **np); (*np)->m_flag = MNOFROM; (*np)->m_have = HAVE_HEADER|HAVE_BODY; (*np)->m_block = mailx_blockof(offs); (*np)->m_offset = mailx_offsetof(offs); if (part) { (*part)++; sz = ip->m_partstring ? strlen(ip->m_partstring) : 0; sz += 20; (*np)->m_partstring = salloc(sz); if (ip->m_partstring) snprintf((*np)->m_partstring, sz, "%s.%u", ip->m_partstring, *part); else snprintf((*np)->m_partstring, sz, "%u", *part); } else (*np)->m_mimecontent = MIME_DISCARD; (*np)->m_parent = ip; if (ip->m_multipart) { for (pp = ip->m_multipart; pp->m_nextpart; pp = pp->m_nextpart); pp->m_nextpart = *np; } else ip->m_multipart = *np; } static void endpart(struct mimepart **np, off_t xoffs, long lines) { off_t offs; offs = mailx_positionof((*np)->m_block, (*np)->m_offset); (*np)->m_size = (*np)->m_xsize = xoffs - offs; (*np)->m_lines = (*np)->m_xlines = lines; *np = NULL; } static void parse822(struct message *zmp, struct mimepart *ip, enum parseflags pf, int level) { int c, lastc = '\n'; size_t count; FILE *ibuf; off_t offs; struct mimepart *np; long lines; if ((ibuf = setinput(&mb, (struct message *)ip, NEED_BODY)) == NULL) return; count = ip->m_size; lines = ip->m_lines; while (count && ((c = getc(ibuf)) != EOF)) { count--; if (c == '\n') { lines--; if (lastc == '\n') break; } lastc = c; } offs = ftell(ibuf); np = csalloc(1, sizeof *np); np->m_flag = MNOFROM; np->m_have = HAVE_HEADER|HAVE_BODY; np->m_block = mailx_blockof(offs); np->m_offset = mailx_offsetof(offs); np->m_size = np->m_xsize = count; np->m_lines = np->m_xlines = lines; np->m_partstring = ip->m_partstring; np->m_parent = ip; ip->m_multipart = np; substdate((struct message *)np); np->m_from = fakefrom((struct message *)np); parsepart(zmp, np, pf, level+1); } static void parsepkcs7(struct message *zmp, struct mimepart *ip, enum parseflags pf, int level) { struct message m, *xmp; struct mimepart *np; char *to, *cc; memcpy(&m, ip, sizeof m); to = hfield("to", zmp); cc = hfield("cc", zmp); if ((xmp = smime_decrypt(&m, to, cc, 0)) != NULL) { np = csalloc(1, sizeof *np); np->m_flag = xmp->m_flag; np->m_have = xmp->m_have; np->m_block = xmp->m_block; np->m_offset = xmp->m_offset; np->m_size = xmp->m_size; np->m_xsize = xmp->m_xsize; np->m_lines = xmp->m_lines; np->m_xlines = xmp->m_xlines; np->m_partstring = ip->m_partstring; if (parsepart(zmp, np, pf, level+1) == OKAY) { np->m_parent = ip; ip->m_multipart = np; } } } static size_t out(char *buf, size_t len, FILE *fp, enum conversion convert, enum sendaction action, char *prefix, size_t prefixlen, off_t *stats, char **restp, size_t *restsizep) { size_t sz, n; char *cp; long lines; sz = 0; if (action == SEND_MBOX || action == SEND_DECRYPT) { cp = buf; n = len; while (n && cp[0] == '>') cp++, n--; if (n >= 5 && cp[0] == 'F' && cp[1] == 'r' && cp[2] == 'o' && cp[3] == 'm' && cp[4] == ' ') { putc('>', fp); sz++; } } sz += mime_write(buf, len, fp, action == SEND_MBOX ? CONV_NONE : convert, action == SEND_TODISP || action == SEND_TODISP_ALL || action == SEND_QUOTE || action == SEND_QUOTE_ALL ? TD_ISPR|TD_ICONV : action == SEND_TOSRCH || action == SEND_TOPIPE ? TD_ICONV : action == SEND_TOFLTR ? TD_DELCTRL : action == SEND_SHOW ? TD_ISPR : TD_NONE, prefix, prefixlen, restp, restsizep); lines = 0; if (stats && stats[0] != -1) { for (cp = buf; cp < &buf[sz]; cp++) if (*cp == '\n') lines++; } addstats(stats, lines, sz); return sz; } static void addstats(off_t *stats, off_t lines, off_t bytes) { if (stats) { if (stats[0] >= 0) stats[0] += lines; stats[1] += bytes; } } /* * Get a file for an attachment. */ static FILE * newfile(struct mimepart *ip, int *ispipe, sighandler_type *oldpipe) { char *f = ip->m_filename; struct str in, out; FILE *fp; *ispipe = 0; if (f != NULL && f != (char *)-1) { in.s = f; in.l = strlen(f); mime_fromhdr(&in, &out, TD_ISPR); memcpy(f, out.s, out.l); *(f + out.l) = '\0'; free(out.s); } if (value("interactive") != NULL) { printf("Enter filename for part %s (%s)", ip->m_partstring ? ip->m_partstring : "?", ip->m_ct_type_plain); f = readtty(catgets(catd, CATSET, 173, ": "), f != (char *)-1 ? f : NULL); } if (f == NULL || f == (char *)-1) return NULL; if (*f == '|') { char *cp; cp = value("SHELL"); if (cp == NULL) cp = SHELL; fp = Popen(f+1, "w", cp, 1); if (fp == NULL) { perror(f); fp = stdout; } else { *oldpipe = safe_signal(SIGPIPE, brokpipe); *ispipe = 1; } } else { if ((fp = Fopen(f, "w")) == NULL) fprintf(stderr, "Cannot open %s\n", f); } return fp; } static char * getpipecmd(char *content) { char *penv, *cp, *cq, *pipecmd; if (content == NULL) return NULL; penv = ac_alloc(strlen(content) + 6); strcpy(penv, "pipe-"); cp = &penv[5]; cq = content; do *cp++ = lowerconv(*cq & 0377); while (*cq++); pipecmd = value(penv); ac_free(penv); return pipecmd; } static FILE * getpipefile(char *pipecmd, FILE **qbuf, int quote) { char *shell; FILE *rbuf = *qbuf; if (pipecmd != NULL) { if (quote) { char *tempPipe; if ((*qbuf = Ftemp(&tempPipe, "Rp", "w+", 0600, 1)) == NULL) { perror(catgets(catd, CATSET, 173, "tmpfile")); *qbuf = rbuf; } unlink(tempPipe); Ftfree(&tempPipe); } if ((shell = value("SHELL")) == NULL) shell = SHELL; if ((rbuf = Popen(pipecmd, "W", shell, fileno(*qbuf))) == NULL) { perror(pipecmd); } else { fflush(*qbuf); if (*qbuf != stdout) fflush(stdout); } } return rbuf; } static void pipecpy(FILE *pipebuf, FILE *outbuf, FILE *origobuf, char *prefix, size_t prefixlen, off_t *stats) { char *line = NULL; size_t linesize = 0, linelen, sz, count; fflush(pipebuf); rewind(pipebuf); count = fsize(pipebuf); while (fgetline(&line, &linesize, &count, &linelen, pipebuf, 0) != NULL) { sz = prefixwrite(line, sizeof *line, linelen, outbuf, prefix, prefixlen); if (outbuf == origobuf) addstats(stats, 1, sz); } if (line) free(line); fclose(pipebuf); } /* * Output a reasonable looking status field. */ static void statusput(const struct message *mp, FILE *obuf, char *prefix, off_t *stats) { char statout[3]; char *cp = statout; if (mp->m_flag & MREAD) *cp++ = 'R'; if ((mp->m_flag & MNEW) == 0) *cp++ = 'O'; *cp = 0; if (statout[0]) fprintf(obuf, "%sStatus: %s\n", prefix == NULL ? "" : prefix, statout); addstats(stats, 1, (prefix ? strlen(prefix) : 0) + 9 + cp - statout); } static void xstatusput(const struct message *mp, FILE *obuf, char *prefix, off_t *stats) { char xstatout[4]; char *xp = xstatout; if (mp->m_flag & MFLAGGED) *xp++ = 'F'; if (mp->m_flag & MANSWERED) *xp++ = 'A'; if (mp->m_flag & MDRAFTED) *xp++ = 'T'; *xp = 0; if (xstatout[0]) fprintf(obuf, "%sX-Status: %s\n", prefix == NULL ? "" : prefix, xstatout); addstats(stats, 1, (prefix ? strlen(prefix) : 0) + 11 + xp - xstatout); } static void put_from_(FILE *fp, struct mimepart *ip) { time_t now; if (ip && ip->m_from) fprintf(fp, "From %s %s\n", ip->m_from, fakedate(ip->m_time)); else { time(&now); fprintf(fp, "From %s %s", myname, ctime(&now)); } } /* * This is fgetline for mbox lines. */ char * foldergets(char **s, size_t *size, size_t *count, size_t *llen, FILE *stream) { char *p, *top; if ((p = fgetline(s, size, count, llen, stream, 0)) == NULL) return NULL; if (*p == '>') { p++; while (*p == '>') p++; if (strncmp(p, "From ", 5) == 0) { /* we got a masked From line */ top = &(*s)[*llen]; p = *s; do p[0] = p[1]; while (++p < top); (*llen)--; } } return *s; } heirloom-mailx-12.5/sendout.c000066400000000000000000001073461155563371200162370ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)sendout.c 2.100 (gritter) 3/1/09"; #endif #endif /* not lint */ #include "rcv.h" #include "extern.h" #include #include #include #include #include #include "md5.h" /* * Mail -- a mail program * * Mail to others. */ static char *send_boundary; static char *getencoding(enum conversion convert); static struct name *fixhead(struct header *hp, struct name *tolist); static int put_signature(FILE *fo, int convert); static int attach_file1(struct attachment *ap, FILE *fo, int dosign); static int attach_file(struct attachment *ap, FILE *fo, int dosign); static int attach_message(struct attachment *ap, FILE *fo, int dosign); static int make_multipart(struct header *hp, int convert, FILE *fi, FILE *fo, const char *contenttype, const char *charset, int dosign); static FILE *infix(struct header *hp, FILE *fi, int dosign); static int savemail(char *name, FILE *fi); static int sendmail_internal(void *v, int recipient_record); static enum okay transfer(struct name *to, struct name *mailargs, FILE *input, struct header *hp); static enum okay start_mta(struct name *to, struct name *mailargs, FILE *input, struct header *hp); static void message_id(FILE *fo, struct header *hp); static int fmt(char *str, struct name *np, FILE *fo, int comma, int dropinvalid, int domime); static int infix_resend(FILE *fi, FILE *fo, struct message *mp, struct name *to, int add_resent); /* * Generate a boundary for MIME multipart messages. */ char * makeboundary(void) { static char bound[70]; time_t now; time(&now); snprintf(bound, sizeof bound, "=_%lx.%s", (long)now, getrandstring(48)); send_boundary = bound; return send_boundary; } /* * Get an encoding flag based on the given string. */ static char * getencoding(enum conversion convert) { switch (convert) { case CONV_7BIT: return "7bit"; case CONV_8BIT: return "8bit"; case CONV_TOQP: return "quoted-printable"; case CONV_TOB64: return "base64"; default: break; } /*NOTREACHED*/ return NULL; } /* * Fix the header by glopping all of the expanded names from * the distribution list into the appropriate fields. */ static struct name * fixhead(struct header *hp, struct name *tolist) { struct name *np; hp->h_to = NULL; hp->h_cc = NULL; hp->h_bcc = NULL; for (np = tolist; np != NULL; np = np->n_flink) if ((np->n_type & GMASK) == GTO) hp->h_to = cat(hp->h_to, nalloc(np->n_fullname, np->n_type|GFULL)); else if ((np->n_type & GMASK) == GCC) hp->h_cc = cat(hp->h_cc, nalloc(np->n_fullname, np->n_type|GFULL)); else if ((np->n_type & GMASK) == GBCC) hp->h_bcc = cat(hp->h_bcc, nalloc(np->n_fullname, np->n_type|GFULL)); return tolist; } /* * Do not change, you get incorrect base64 encodings else! */ #define INFIX_BUF 972 /* * Put the signature file at fo. */ static int put_signature(FILE *fo, int convert) { char *sig, buf[INFIX_BUF], c = '\n'; FILE *fsig; size_t sz; sig = value("signature"); if (sig == NULL || *sig == '\0') return 0; else sig = expand(sig); if ((fsig = Fopen(sig, "r")) == NULL) { perror(sig); return -1; } while ((sz = fread(buf, sizeof *buf, INFIX_BUF, fsig)) != 0) { c = buf[sz - 1]; if (mime_write(buf, sz, fo, convert, TD_NONE, NULL, (size_t)0, NULL, NULL) == 0) { perror(sig); Fclose(fsig); return -1; } } if (ferror(fsig)) { perror(sig); Fclose(fsig); return -1; } Fclose(fsig); if (c != '\n') putc('\n', fo); return 0; } /* * Write an attachment to the file buffer, converting to MIME. */ static int attach_file1(struct attachment *ap, FILE *fo, int dosign) { FILE *fi; char *charset = NULL, *contenttype = NULL, *basename; enum conversion convert = CONV_TOB64; int err = 0; enum mimeclean isclean; size_t sz; char *buf; size_t bufsize, count; int lastc = EOF; #ifdef HAVE_ICONV char *tcs; #endif if ((fi = Fopen(ap->a_name, "r")) == NULL) { perror(ap->a_name); return -1; } if ((basename = strrchr(ap->a_name, '/')) == NULL) basename = ap->a_name; else basename++; if (ap->a_content_type) contenttype = ap->a_content_type; else contenttype = mime_filecontent(basename); if (ap->a_charset) charset = ap->a_charset; convert = get_mime_convert(fi, &contenttype, &charset, &isclean, dosign); fprintf(fo, "\n--%s\n" "Content-Type: %s", send_boundary, contenttype); if (charset == NULL) putc('\n', fo); else fprintf(fo, ";\n charset=%s\n", charset); if (ap->a_content_disposition == NULL) ap->a_content_disposition = "attachment"; fprintf(fo, "Content-Transfer-Encoding: %s\n" "Content-Disposition: %s;\n" " filename=\"", getencoding(convert), ap->a_content_disposition); mime_write(basename, strlen(basename), fo, CONV_TOHDR, TD_NONE, NULL, (size_t)0, NULL, NULL); fwrite("\"\n", sizeof (char), 2, fo); if (ap->a_content_id) fprintf(fo, "Content-ID: %s\n", ap->a_content_id); if (ap->a_content_description) fprintf(fo, "Content-Description: %s\n", ap->a_content_description); putc('\n', fo); #ifdef HAVE_ICONV if (iconvd != (iconv_t)-1) { iconv_close(iconvd); iconvd = (iconv_t)-1; } tcs = gettcharset(); if ((isclean & (MIME_HASNUL|MIME_CTRLCHAR)) == 0 && ascncasecmp(contenttype, "text/", 5) == 0 && isclean & MIME_HIGHBIT && charset != NULL) { if ((iconvd = iconv_open_ft(charset, tcs)) == (iconv_t)-1 && errno != 0) { if (errno == EINVAL) fprintf(stderr, catgets(catd, CATSET, 179, "Cannot convert from %s to %s\n"), tcs, charset); else perror("iconv_open"); Fclose(fi); return -1; } } #endif /* HAVE_ICONV */ buf = smalloc(bufsize = INFIX_BUF); if (convert == CONV_TOQP #ifdef HAVE_ICONV || iconvd != (iconv_t)-1 #endif ) { fflush(fi); count = fsize(fi); } for (;;) { if (convert == CONV_TOQP #ifdef HAVE_ICONV || iconvd != (iconv_t)-1 #endif ) { if (fgetline(&buf, &bufsize, &count, &sz, fi, 0) == NULL) break; } else { if ((sz = fread(buf, sizeof *buf, bufsize, fi)) == 0) break; } lastc = buf[sz-1]; if (mime_write(buf, sz, fo, convert, TD_ICONV, NULL, (size_t)0, NULL, NULL) == 0) err = -1; } if (convert == CONV_TOQP && lastc != '\n') fwrite("=\n", 1, 2, fo); if (ferror(fi)) err = -1; Fclose(fi); free(buf); return err; } /* * Try out different character set conversions to attach a file. */ static int attach_file(struct attachment *ap, FILE *fo, int dosign) { char *_wantcharset, *charsets, *ncs; size_t offs = ftell(fo); if (ap->a_charset || (charsets = value("sendcharsets")) == NULL) return attach_file1(ap, fo, dosign); _wantcharset = wantcharset; wantcharset = savestr(charsets); loop: if ((ncs = strchr(wantcharset, ',')) != NULL) *ncs++ = '\0'; try: if (attach_file1(ap, fo, dosign) != 0) { if (errno == EILSEQ || errno == EINVAL) { if (ncs && *ncs) { wantcharset = ncs; clearerr(fo); fseek(fo, offs, SEEK_SET); goto loop; } if (wantcharset) { if (wantcharset == (char *)-1) wantcharset = NULL; else { wantcharset = (char *)-1; clearerr(fo); fseek(fo, offs, SEEK_SET); goto try; } } } } wantcharset = _wantcharset; return 0; } /* * Attach a message to the file buffer. */ static int attach_message(struct attachment *ap, FILE *fo, int dosign) { struct message *mp; fprintf(fo, "\n--%s\n" "Content-Type: message/rfc822\n" "Content-Disposition: inline\n\n", send_boundary); mp = &message[ap->a_msgno - 1]; touch(mp); if (send(mp, fo, 0, NULL, SEND_RFC822, NULL) < 0) return -1; return 0; } /* * Generate the body of a MIME multipart message. */ static int make_multipart(struct header *hp, int convert, FILE *fi, FILE *fo, const char *contenttype, const char *charset, int dosign) { struct attachment *att; fputs("This is a multi-part message in MIME format.\n", fo); if (fsize(fi) != 0) { char *buf, c = '\n'; size_t sz, bufsize, count; fprintf(fo, "\n--%s\n", send_boundary); fprintf(fo, "Content-Type: %s", contenttype); if (charset) fprintf(fo, "; charset=%s", charset); fprintf(fo, "\nContent-Transfer-Encoding: %s\n" "Content-Disposition: inline\n\n", getencoding(convert)); buf = smalloc(bufsize = INFIX_BUF); if (convert == CONV_TOQP #ifdef HAVE_ICONV || iconvd != (iconv_t)-1 #endif /* HAVE_ICONV */ ) { fflush(fi); count = fsize(fi); } for (;;) { if (convert == CONV_TOQP #ifdef HAVE_ICONV || iconvd != (iconv_t)-1 #endif /* HAVE_ICONV */ ) { if (fgetline(&buf, &bufsize, &count, &sz, fi, 0) == NULL) break; } else { sz = fread(buf, sizeof *buf, bufsize, fi); if (sz == 0) break; } c = buf[sz - 1]; if (mime_write(buf, sz, fo, convert, TD_ICONV, NULL, (size_t)0, NULL, NULL) == 0) { free(buf); return -1; } } free(buf); if (ferror(fi)) return -1; if (c != '\n') putc('\n', fo); if (charset != NULL) put_signature(fo, convert); } for (att = hp->h_attach; att != NULL; att = att->a_flink) { if (att->a_msgno) { if (attach_message(att, fo, dosign) != 0) return -1; } else { if (attach_file(att, fo, dosign) != 0) return -1; } } /* the final boundary with two attached dashes */ fprintf(fo, "\n--%s--\n", send_boundary); return 0; } /* * Prepend a header in front of the collected stuff * and return the new file. */ static FILE * infix(struct header *hp, FILE *fi, int dosign) { FILE *nfo, *nfi; char *tempMail; #ifdef HAVE_ICONV char *tcs, *convhdr = NULL; #endif enum mimeclean isclean; enum conversion convert; char *charset = NULL, *contenttype = NULL; int lastc = EOF; if ((nfo = Ftemp(&tempMail, "Rs", "w", 0600, 1)) == NULL) { perror(catgets(catd, CATSET, 178, "temporary mail file")); return(NULL); } if ((nfi = Fopen(tempMail, "r")) == NULL) { perror(tempMail); Fclose(nfo); return(NULL); } rm(tempMail); Ftfree(&tempMail); convert = get_mime_convert(fi, &contenttype, &charset, &isclean, dosign); #ifdef HAVE_ICONV tcs = gettcharset(); if ((convhdr = need_hdrconv(hp, GTO|GSUBJECT|GCC|GBCC|GIDENT)) != 0) { if (iconvd != (iconv_t)-1) iconv_close(iconvd); if ((iconvd = iconv_open_ft(convhdr, tcs)) == (iconv_t)-1 && errno != 0) { if (errno == EINVAL) fprintf(stderr, catgets(catd, CATSET, 179, "Cannot convert from %s to %s\n"), tcs, convhdr); else perror("iconv_open"); Fclose(nfo); return NULL; } } #endif /* HAVE_ICONV */ if (puthead(hp, nfo, GTO|GSUBJECT|GCC|GBCC|GNL|GCOMMA|GUA|GMIME |GMSGID|GIDENT|GREF|GDATE, SEND_MBOX, convert, contenttype, charset)) { Fclose(nfo); Fclose(nfi); #ifdef HAVE_ICONV if (iconvd != (iconv_t)-1) { iconv_close(iconvd); iconvd = (iconv_t)-1; } #endif return NULL; } #ifdef HAVE_ICONV if (convhdr && iconvd != (iconv_t)-1) { iconv_close(iconvd); iconvd = (iconv_t)-1; } if ((isclean & (MIME_HASNUL|MIME_CTRLCHAR)) == 0 && ascncasecmp(contenttype, "text/", 5) == 0 && isclean & MIME_HIGHBIT && charset != NULL) { if (iconvd != (iconv_t)-1) iconv_close(iconvd); if ((iconvd = iconv_open_ft(charset, tcs)) == (iconv_t)-1 && errno != 0) { if (errno == EINVAL) fprintf(stderr, catgets(catd, CATSET, 179, "Cannot convert from %s to %s\n"), tcs, charset); else perror("iconv_open"); Fclose(nfo); return NULL; } } #endif if (hp->h_attach != NULL) { if (make_multipart(hp, convert, fi, nfo, contenttype, charset, dosign) != 0) { Fclose(nfo); Fclose(nfi); #ifdef HAVE_ICONV if (iconvd != (iconv_t)-1) { iconv_close(iconvd); iconvd = (iconv_t)-1; } #endif return NULL; } } else { size_t sz, bufsize, count; char *buf; if (convert == CONV_TOQP #ifdef HAVE_ICONV || iconvd != (iconv_t)-1 #endif /* HAVE_ICONV */ ) { fflush(fi); count = fsize(fi); } buf = smalloc(bufsize = INFIX_BUF); for (;;) { if (convert == CONV_TOQP #ifdef HAVE_ICONV || iconvd != (iconv_t)-1 #endif /* HAVE_ICONV */ ) { if (fgetline(&buf, &bufsize, &count, &sz, fi, 0) == NULL) break; } else { sz = fread(buf, sizeof *buf, bufsize, fi); if (sz == 0) break; } lastc = buf[sz - 1]; if (mime_write(buf, sz, nfo, convert, TD_ICONV, NULL, (size_t)0, NULL, NULL) == 0) { Fclose(nfo); Fclose(nfi); #ifdef HAVE_ICONV if (iconvd != (iconv_t)-1) { iconv_close(iconvd); iconvd = (iconv_t)-1; } #endif free(buf); return NULL; } } if (convert == CONV_TOQP && lastc != '\n') fwrite("=\n", 1, 2, nfo); free(buf); if (ferror(fi)) { Fclose(nfo); Fclose(nfi); #ifdef HAVE_ICONV if (iconvd != (iconv_t)-1) { iconv_close(iconvd); iconvd = (iconv_t)-1; } #endif return NULL; } if (charset != NULL) put_signature(nfo, convert); } #ifdef HAVE_ICONV if (iconvd != (iconv_t)-1) { iconv_close(iconvd); iconvd = (iconv_t)-1; } #endif fflush(nfo); if (ferror(nfo)) { perror(catgets(catd, CATSET, 180, "temporary mail file")); Fclose(nfo); Fclose(nfi); return NULL; } Fclose(nfo); Fclose(fi); fflush(nfi); rewind(nfi); return(nfi); } /* * Save the outgoing mail on the passed file. */ /*ARGSUSED*/ static int savemail(char *name, FILE *fi) { FILE *fo; char *buf; size_t bufsize, buflen, count; char *p; time_t now; int prependnl = 0; int error = 0; buf = smalloc(bufsize = LINESIZE); time(&now); if ((fo = Zopen(name, "a+", NULL)) == NULL) { if ((fo = Zopen(name, "wx", NULL)) == NULL) { perror(name); free(buf); return (-1); } } else { if (fseek(fo, -2L, SEEK_END) == 0) { switch (fread(buf, sizeof *buf, 2, fo)) { case 2: if (buf[1] != '\n') { prependnl = 1; break; } /*FALLTHRU*/ case 1: if (buf[0] != '\n') prependnl = 1; break; default: if (ferror(fo)) { perror(name); free(buf); return -1; } } fflush(fo); if (prependnl) { putc('\n', fo); fflush(fo); } } } fprintf(fo, "From %s %s", myname, ctime(&now)); buflen = 0; fflush(fi); rewind(fi); count = fsize(fi); while (fgetline(&buf, &bufsize, &count, &buflen, fi, 0) != NULL) { if (*buf == '>') { p = buf + 1; while (*p == '>') p++; if (strncmp(p, "From ", 5) == 0) /* we got a masked From line */ putc('>', fo); } else if (strncmp(buf, "From ", 5) == 0) putc('>', fo); fwrite(buf, sizeof *buf, buflen, fo); } if (buflen && *(buf + buflen - 1) != '\n') putc('\n', fo); putc('\n', fo); fflush(fo); if (ferror(fo)) { perror(name); error = -1; } if (Fclose(fo) != 0) error = -1; fflush(fi); rewind(fi); /* * OpenBSD 3.2 and NetBSD 1.5.2 were reported not to * reset the kernel file offset after the calls above, * a clear violation of IEEE Std 1003.1, 1996, 8.2.3.7. * So do it 'manually'. */ lseek(fileno(fi), 0, SEEK_SET); free(buf); return error; } /* * Interface between the argument list and the mail1 routine * which does all the dirty work. */ int mail(struct name *to, struct name *cc, struct name *bcc, struct name *smopts, char *subject, struct attachment *attach, char *quotefile, int recipient_record, int tflag, int Eflag) { struct header head; struct str in, out; memset(&head, 0, sizeof head); /* The given subject may be in RFC1522 format. */ if (subject != NULL) { in.s = subject; in.l = strlen(subject); mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV); head.h_subject = out.s; } if (tflag == 0) { head.h_to = to; head.h_cc = cc; head.h_bcc = bcc; } head.h_attach = attach; head.h_smopts = smopts; mail1(&head, 0, NULL, quotefile, recipient_record, 0, tflag, Eflag); if (subject != NULL) free(out.s); return(0); } /* * Send mail to a bunch of user names. The interface is through * the mail routine below. */ static int sendmail_internal(void *v, int recipient_record) { int Eflag; char *str = v; struct header head; memset(&head, 0, sizeof head); head.h_to = extract(str, GTO|GFULL); Eflag = value("skipemptybody") != NULL; mail1(&head, 0, NULL, NULL, recipient_record, 0, 0, Eflag); return(0); } int sendmail(void *v) { return sendmail_internal(v, 0); } int Sendmail(void *v) { return sendmail_internal(v, 1); } static enum okay transfer(struct name *to, struct name *mailargs, FILE *input, struct header *hp) { char o[LINESIZE], *cp; struct name *np, *nt; int cnt = 0; FILE *ef; enum okay ok = OKAY; np = to; while (np) { snprintf(o, sizeof o, "smime-encrypt-%s", np->n_name); if ((cp = value(o)) != NULL) { if ((ef = smime_encrypt(input, cp, np->n_name)) != 0) { nt = nalloc(np->n_name, np->n_type & ~(GFULL|GSKIN)); if (start_mta(nt, mailargs, ef, hp) != OKAY) ok = STOP; Fclose(ef); } else { fprintf(stderr, "Message not sent to <%s>\n", np->n_name); senderr++; } rewind(input); if (np->n_flink) np->n_flink->n_blink = np->n_blink; if (np->n_blink) np->n_blink->n_flink = np->n_flink; if (np == to) to = np->n_flink; np = np->n_flink; } else { cnt++; np = np->n_flink; } } if (cnt) { if (value("smime-force-encryption") || start_mta(to, mailargs, input, hp) != OKAY) ok = STOP; } return ok; } /* * Start the Mail Transfer Agent * mailing to namelist and stdin redirected to input. */ static enum okay start_mta(struct name *to, struct name *mailargs, FILE *input, struct header *hp) { char **args = NULL, **t; pid_t pid; sigset_t nset; char *cp, *smtp; char *user = NULL, *password = NULL, *skinned = NULL; enum okay ok = STOP; #ifdef HAVE_SOCKETS struct termios otio; int reset_tio; #endif /* HAVE_SOCKETS */ if ((smtp = value("smtp")) == NULL) { args = unpack(cat(mailargs, to)); if (debug || value("debug")) { printf(catgets(catd, CATSET, 181, "Sendmail arguments:")); for (t = args; *t != NULL; t++) printf(" \"%s\"", *t); printf("\n"); return OKAY; } } #ifdef HAVE_SOCKETS if (smtp != NULL) { skinned = skin(myorigin(hp)); if ((user = smtp_auth_var("-user", skinned)) != NULL && (password = smtp_auth_var("-password", skinned)) == NULL) password = getpassword(&otio, &reset_tio, NULL); } #endif /* HAVE_SOCKETS */ /* * Fork, set up the temporary mail file as standard * input for "mail", and exec with the user list we generated * far above. */ if ((pid = fork()) == -1) { perror("fork"); savedeadletter(input); senderr++; return STOP; } if (pid == 0) { sigemptyset(&nset); sigaddset(&nset, SIGHUP); sigaddset(&nset, SIGINT); sigaddset(&nset, SIGQUIT); sigaddset(&nset, SIGTSTP); sigaddset(&nset, SIGTTIN); sigaddset(&nset, SIGTTOU); freopen("/dev/null", "r", stdin); if (smtp != NULL) { prepare_child(&nset, 0, 1); if (smtp_mta(smtp, to, input, hp, user, password, skinned) == 0) _exit(0); } else { prepare_child(&nset, fileno(input), -1); if ((cp = value("sendmail")) != NULL) cp = expand(cp); else cp = SENDMAIL; execv(cp, args); perror(cp); } savedeadletter(input); fputs(catgets(catd, CATSET, 182, ". . . message not sent.\n"), stderr); _exit(1); } if (value("verbose") != NULL || value("sendwait") || debug || value("debug")) { if (wait_child(pid) == 0) ok = OKAY; else senderr++; } else { ok = OKAY; free_child(pid); } return ok; } /* * Record outgoing mail if instructed to do so. */ static enum okay mightrecord(FILE *fp, struct name *to, int recipient_record) { char *cp, *cq, *ep; if (recipient_record) { cq = skin(to->n_name); cp = salloc(strlen(cq) + 1); strcpy(cp, cq); for (cq = cp; *cq && *cq != '@'; cq++); *cq = '\0'; } else cp = value("record"); if (cp != NULL) { ep = expand(cp); if (value("outfolder") && *ep != '/' && *ep != '+' && which_protocol(ep) == PROTO_FILE) { cq = salloc(strlen(cp) + 2); cq[0] = '+'; strcpy(&cq[1], cp); cp = cq; ep = expand(cp); } if (savemail(ep, fp) != 0) { fprintf(stderr, "Error while saving message to %s - " "message not sent\n", ep); rewind(fp); exit_status |= 1; savedeadletter(fp); return STOP; } } return OKAY; } /* * Mail a message on standard input to the people indicated * in the passed header. (Internal interface). */ enum okay mail1(struct header *hp, int printheaders, struct message *quote, char *quotefile, int recipient_record, int doprefix, int tflag, int Eflag) { struct name *to; FILE *mtf, *nmtf; enum okay ok = STOP; int dosign = -1; char *charsets, *ncs = NULL, *cp; #ifdef notdef if ((hp->h_to = checkaddrs(hp->h_to)) == NULL) { senderr++; return STOP; } #endif if ((cp = value("autocc")) != NULL && *cp) hp->h_cc = cat(hp->h_cc, checkaddrs(sextract(cp, GCC|GFULL))); if ((cp = value("autobcc")) != NULL && *cp) hp->h_bcc = cat(hp->h_bcc, checkaddrs(sextract(cp, GBCC|GFULL))); /* * Collect user's mail from standard input. * Get the result as mtf. */ if ((mtf = collect(hp, printheaders, quote, quotefile, doprefix, tflag)) == NULL) return STOP; if (value("interactive") != NULL) { if (((value("bsdcompat") || value("askatend")) && (value("askcc") != NULL || value("askbcc") != NULL)) || value("askattach") != NULL || value("asksign") != NULL) { if (value("askcc") != NULL) grabh(hp, GCC, 1); if (value("askbcc") != NULL) grabh(hp, GBCC, 1); if (value("askattach") != NULL) hp->h_attach = edit_attachments(hp->h_attach); if (value("asksign") != NULL) dosign = yorn("Sign this message (y/n)? "); } else { printf(catgets(catd, CATSET, 183, "EOT\n")); fflush(stdout); } } if (fsize(mtf) == 0) { if (Eflag) goto out; if (hp->h_subject == NULL) printf(catgets(catd, CATSET, 184, "No message, no subject; hope that's ok\n")); else if (value("bsdcompat") || value("bsdmsgs")) printf(catgets(catd, CATSET, 185, "Null message body; hope that's ok\n")); } if (dosign < 0) { if (value("smime-sign") != NULL) dosign = 1; else dosign = 0; } /* * Now, take the user names from the combined * to and cc lists and do all the alias * processing. */ senderr = 0; if ((to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc)))) == NULL) { printf(catgets(catd, CATSET, 186, "No recipients specified\n")); senderr++; } to = fixhead(hp, to); if (hp->h_charset) { wantcharset = hp->h_charset; goto try; } hloop: wantcharset = NULL; if ((charsets = value("sendcharsets")) != NULL) { wantcharset = savestr(charsets); loop: if ((ncs = strchr(wantcharset, ',')) != NULL) *ncs++ = '\0'; } try: if ((nmtf = infix(hp, mtf, dosign)) == NULL) { if (hp->h_charset && (errno == EILSEQ || errno == EINVAL)) { rewind(mtf); hp->h_charset = NULL; goto hloop; } if (errno == EILSEQ || errno == EINVAL) { if (ncs && *ncs) { rewind(mtf); wantcharset = ncs; goto loop; } if (wantcharset && value("interactive") == NULL) { if (wantcharset == (char *)-1) wantcharset = NULL; else { rewind(mtf); wantcharset = (char *)-1; goto try; } } } /* fprintf(stderr, ". . . message lost, sorry.\n"); */ perror(""); fail: senderr++; rewind(mtf); savedeadletter(mtf); fputs(catgets(catd, CATSET, 187, ". . . message not sent.\n"), stderr); return STOP; } mtf = nmtf; if (dosign) { if ((nmtf = smime_sign(mtf, hp)) == NULL) goto fail; Fclose(mtf); mtf = nmtf; } /* * Look through the recipient list for names with /'s * in them which we write to as files directly. */ to = outof(to, mtf, hp); if (senderr) savedeadletter(mtf); to = elide(to); if (count(to) == 0) { if (senderr == 0) ok = OKAY; goto out; } if (mightrecord(mtf, to, recipient_record) != OKAY) goto out; ok = transfer(to, hp->h_smopts, mtf, hp); out: Fclose(mtf); return ok; } /* * Create a Message-Id: header field. * Use either the host name or the from address. */ static void message_id(FILE *fo, struct header *hp) { char *cp; time_t now; time(&now); if ((cp = value("hostname")) != NULL) fprintf(fo, "Message-ID: <%lx.%s@%s>\n", (long)now, getrandstring(24), cp); else if ((cp = skin(myorigin(hp))) != NULL && strchr(cp, '@') != NULL) fprintf(fo, "Message-ID: <%lx.%s%%%s>\n", (long)now, getrandstring(16), cp); } static const char *weekday_names[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; const char *month_names[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL }; /* * Create a Date: header field. * We compare the localtime() and gmtime() results to get the timezone, * because numeric timezones are easier to read and because $TZ is * not set on most GNU systems. */ int mkdate(FILE *fo, const char *field) { time_t t; struct tm *tmptr; int tzdiff, tzdiff_hour, tzdiff_min; time(&t); tzdiff = t - mktime(gmtime(&t)); tzdiff_hour = (int)(tzdiff / 60); tzdiff_min = tzdiff_hour % 60; tzdiff_hour /= 60; tmptr = localtime(&t); if (tmptr->tm_isdst > 0) tzdiff_hour++; return fprintf(fo, "%s: %s, %02d %s %04d %02d:%02d:%02d %+05d\n", field, weekday_names[tmptr->tm_wday], tmptr->tm_mday, month_names[tmptr->tm_mon], tmptr->tm_year + 1900, tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec, tzdiff_hour * 100 + tzdiff_min); } static enum okay putname(char *line, enum gfield w, enum sendaction action, int *gotcha, char *prefix, FILE *fo, struct name **xp) { struct name *np; np = sextract(line, GEXTRA|GFULL); if (xp) *xp = np; if (np == NULL) return 0; if (fmt(prefix, np, fo, w&(GCOMMA|GFILES), 0, action != SEND_TODISP)) return 1; if (gotcha) (*gotcha)++; return 0; } #define FMT_CC_AND_BCC { \ if (hp->h_cc != NULL && w & GCC) { \ if (fmt("Cc:", hp->h_cc, fo, \ w&(GCOMMA|GFILES), 0, \ action!=SEND_TODISP)) \ return 1; \ gotcha++; \ } \ if (hp->h_bcc != NULL && w & GBCC) { \ if (fmt("Bcc:", hp->h_bcc, fo, \ w&(GCOMMA|GFILES), 0, \ action!=SEND_TODISP)) \ return 1; \ gotcha++; \ } \ } /* * Dump the to, subject, cc header on the * passed file buffer. */ int puthead(struct header *hp, FILE *fo, enum gfield w, enum sendaction action, enum conversion convert, char *contenttype, char *charset) { int gotcha; char *addr/*, *cp*/; int stealthmua; struct name *np, *fromfield = NULL, *senderfield = NULL; if (value("stealthmua")) stealthmua = 1; else stealthmua = 0; gotcha = 0; if (w & GDATE) { mkdate(fo, "Date"), gotcha++; } if (w & GIDENT) { if (hp->h_from != NULL) { if (fmt("From:", hp->h_from, fo, w&(GCOMMA|GFILES), 0, action!=SEND_TODISP)) return 1; gotcha++; fromfield = hp->h_from; } else if ((addr = myaddrs(hp)) != NULL) if (putname(addr, w, action, &gotcha, "From:", fo, &fromfield)) return 1; if (((addr = hp->h_organization) != NULL || (addr = value("ORGANIZATION")) != NULL) && strlen(addr) > 0) { fwrite("Organization: ", sizeof (char), 14, fo); if (mime_write(addr, strlen(addr), fo, action == SEND_TODISP ? CONV_NONE:CONV_TOHDR, action == SEND_TODISP ? TD_ISPR|TD_ICONV:TD_ICONV, NULL, (size_t)0, NULL, NULL) == 0) return 1; gotcha++; putc('\n', fo); } if (hp->h_replyto != NULL) { if (fmt("Reply-To:", hp->h_replyto, fo, w&(GCOMMA|GFILES), 0, action!=SEND_TODISP)) return 1; gotcha++; } else if ((addr = value("replyto")) != NULL) if (putname(addr, w, action, &gotcha, "Reply-To:", fo, NULL)) return 1; if (hp->h_sender != NULL) { if (fmt("Sender:", hp->h_sender, fo, w&(GCOMMA|GFILES), 0, action!=SEND_TODISP)) return 1; gotcha++; senderfield = hp->h_sender; } else if ((addr = value("sender")) != NULL) if (putname(addr, w, action, &gotcha, "Sender:", fo, &senderfield)) return 1; if (check_from_and_sender(fromfield, senderfield)) return 1; } if (hp->h_to != NULL && w & GTO) { if (fmt("To:", hp->h_to, fo, w&(GCOMMA|GFILES), 0, action!=SEND_TODISP)) return 1; gotcha++; } if (value("bsdcompat") == NULL && value("bsdorder") == NULL) FMT_CC_AND_BCC if (hp->h_subject != NULL && w & GSUBJECT) { fwrite("Subject: ", sizeof (char), 9, fo); if (ascncasecmp(hp->h_subject, "re: ", 4) == 0) { fwrite("Re: ", sizeof (char), 4, fo); if (strlen(hp->h_subject + 4) > 0 && mime_write(hp->h_subject + 4, strlen(hp->h_subject + 4), fo, action == SEND_TODISP ? CONV_NONE:CONV_TOHDR, action == SEND_TODISP ? TD_ISPR|TD_ICONV:TD_ICONV, NULL, (size_t)0, NULL, NULL) == 0) return 1; } else if (*hp->h_subject) { if (mime_write(hp->h_subject, strlen(hp->h_subject), fo, action == SEND_TODISP ? CONV_NONE:CONV_TOHDR, action == SEND_TODISP ? TD_ISPR|TD_ICONV:TD_ICONV, NULL, (size_t)0, NULL, NULL) == 0) return 1; } gotcha++; fwrite("\n", sizeof (char), 1, fo); } if (value("bsdcompat") || value("bsdorder")) FMT_CC_AND_BCC if (w & GMSGID && stealthmua == 0) message_id(fo, hp), gotcha++; if (hp->h_ref != NULL && w & GREF) { fmt("References:", hp->h_ref, fo, 0, 1, 0); if ((np = hp->h_ref) != NULL && np->n_name) { while (np->n_flink) np = np->n_flink; if (mime_name_invalid(np->n_name, 0) == 0) { fprintf(fo, "In-Reply-To: %s\n", np->n_name); gotcha++; } } } if (w & GUA && stealthmua == 0) fprintf(fo, "User-Agent: Heirloom mailx %s\n", version), gotcha++; if (w & GMIME) { fputs("MIME-Version: 1.0\n", fo), gotcha++; if (hp->h_attach != NULL) { makeboundary(); fprintf(fo, "Content-Type: multipart/mixed;\n" " boundary=\"%s\"\n", send_boundary); } else { fprintf(fo, "Content-Type: %s", contenttype); if (charset) fprintf(fo, "; charset=%s", charset); fprintf(fo, "\nContent-Transfer-Encoding: %s\n", getencoding(convert)); } } if (gotcha && w & GNL) putc('\n', fo); return(0); } /* * Format the given header line to not exceed 72 characters. */ static int fmt(char *str, struct name *np, FILE *fo, int flags, int dropinvalid, int domime) { int col, len, count = 0; int is_to = 0, comma; comma = flags&GCOMMA ? 1 : 0; col = strlen(str); if (col) { fwrite(str, sizeof *str, strlen(str), fo); if ((flags&GFILES) == 0 && col == 3 && asccasecmp(str, "to:") == 0 || col == 3 && asccasecmp(str, "cc:") == 0 || col == 4 && asccasecmp(str, "bcc:") == 0 || col == 10 && asccasecmp(str, "Resent-To:") == 0) is_to = 1; } for (; np != NULL; np = np->n_flink) { if (is_to && is_fileaddr(np->n_name)) continue; if (np->n_flink == NULL) comma = 0; if (mime_name_invalid(np->n_name, !dropinvalid)) { if (dropinvalid) continue; else return 1; } len = strlen(np->n_fullname); col++; /* for the space */ if (count && col + len + comma > 72 && col > 1) { fputs("\n ", fo); col = 1; } else putc(' ', fo); len = mime_write(np->n_fullname, len, fo, domime?CONV_TOHDR_A:CONV_NONE, TD_ICONV, NULL, (size_t)0, NULL, NULL); if (comma && !(is_to && is_fileaddr(np->n_flink->n_name))) putc(',', fo); col += len + comma; count++; } putc('\n', fo); return 0; } /* * Rewrite a message for resending, adding the Resent-Headers. */ static int infix_resend(FILE *fi, FILE *fo, struct message *mp, struct name *to, int add_resent) { size_t count; char *buf = NULL, *cp/*, *cp2*/; size_t c, bufsize = 0; struct name *fromfield = NULL, *senderfield = NULL; count = mp->m_size; /* * Write the Resent-Fields. */ if (add_resent) { fputs("Resent-", fo); mkdate(fo, "Date"); if ((cp = myaddrs(NULL)) != NULL) { if (putname(cp, GCOMMA, SEND_MBOX, NULL, "Resent-From:", fo, &fromfield)) return 1; } if ((cp = value("sender")) != NULL) { if (putname(cp, GCOMMA, SEND_MBOX, NULL, "Resent-Sender:", fo, &senderfield)) return 1; } #ifdef notdef /* * RFC 2822 disallows generation of this field. */ cp = value("replyto"); if (cp != NULL) { if (mime_name_invalid(cp, 1)) { if (buf) free(buf); return 1; } fwrite("Resent-Reply-To: ", sizeof (char), 17, fo); mime_write(cp, strlen(cp), fo, CONV_TOHDR_A, TD_ICONV, NULL, (size_t)0, NULL, NULL); putc('\n', fo); } #endif /* notdef */ if (fmt("Resent-To:", to, fo, 1, 1, 0)) { if (buf) free(buf); return 1; } if (value("stealthmua") == NULL) { fputs("Resent-", fo); message_id(fo, NULL); } } if (check_from_and_sender(fromfield, senderfield)) return 1; /* * Write the original headers. */ while (count > 0) { if ((cp = foldergets(&buf, &bufsize, &count, &c, fi)) == NULL) break; if (ascncasecmp("status: ", buf, 8) != 0 && strncmp("From ", buf, 5) != 0) { fwrite(buf, sizeof *buf, c, fo); } if (count > 0 && *buf == '\n') break; } /* * Write the message body. */ while (count > 0) { if (foldergets(&buf, &bufsize, &count, &c, fi) == NULL) break; if (count == 0 && *buf == '\n') break; fwrite(buf, sizeof *buf, c, fo); } if (buf) free(buf); if (ferror(fo)) { perror(catgets(catd, CATSET, 188, "temporary mail file")); return 1; } return 0; } enum okay resend_msg(struct message *mp, struct name *to, int add_resent) { FILE *ibuf, *nfo, *nfi; char *tempMail; struct header head; enum okay ok = STOP; memset(&head, 0, sizeof head); if ((to = checkaddrs(to)) == NULL) { senderr++; return STOP; } if ((nfo = Ftemp(&tempMail, "Rs", "w", 0600, 1)) == NULL) { senderr++; perror(catgets(catd, CATSET, 189, "temporary mail file")); return STOP; } if ((nfi = Fopen(tempMail, "r")) == NULL) { senderr++; perror(tempMail); return STOP; } rm(tempMail); Ftfree(&tempMail); if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL) return STOP; head.h_to = to; to = fixhead(&head, to); if (infix_resend(ibuf, nfo, mp, head.h_to, add_resent) != 0) { senderr++; rewind(nfo); savedeadletter(nfi); fputs(catgets(catd, CATSET, 190, ". . . message not sent.\n"), stderr); Fclose(nfo); Fclose(nfi); return STOP; } fflush(nfo); rewind(nfo); Fclose(nfo); to = outof(to, nfi, &head); if (senderr) savedeadletter(nfi); to = elide(to); if (count(to) != 0) { if (value("record-resent") == NULL || mightrecord(nfi, to, 0) == OKAY) ok = transfer(to, head.h_smopts, nfi, NULL); } else if (senderr == 0) ok = OKAY; Fclose(nfi); return ok; } heirloom-mailx-12.5/smtp.c000066400000000000000000000267441155563371200155430ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 2000 * Gunnar Ritter. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Gunnar Ritter * and his contributors. * 4. Neither the name of Gunnar Ritter nor the names of his contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)smtp.c 2.43 (gritter) 8/4/07"; #endif #endif /* not lint */ #include "rcv.h" #include #ifdef HAVE_SOCKETS #include #include #include #ifdef HAVE_ARPA_INET_H #include #endif /* HAVE_ARPA_INET_H */ #endif /* HAVE_SOCKETS */ #include #include #include "extern.h" #include "md5.h" /* * Mail -- a mail program * * SMTP client and other internet related functions. */ #ifdef HAVE_SOCKETS static int verbose; static int _debug; #endif /* * Return our hostname. */ char * nodename(int mayoverride) { static char *hostname; char *hn; struct utsname ut; #ifdef HAVE_SOCKETS #ifdef HAVE_IPv6_FUNCS struct addrinfo hints, *res; #else /* !HAVE_IPv6_FUNCS */ struct hostent *hent; #endif /* !HAVE_IPv6_FUNCS */ #endif /* HAVE_SOCKETS */ if (mayoverride && (hn = value("hostname")) != NULL && *hn) { free(hostname); hostname = sstrdup(hn); } if (hostname == NULL) { uname(&ut); hn = ut.nodename; #ifdef HAVE_SOCKETS #ifdef HAVE_IPv6_FUNCS memset(&hints, 0, sizeof hints); hints.ai_socktype = SOCK_DGRAM; /* dummy */ hints.ai_flags = AI_CANONNAME; if (getaddrinfo(hn, "0", &hints, &res) == 0) { if (res->ai_canonname) { hn = salloc(strlen(res->ai_canonname) + 1); strcpy(hn, res->ai_canonname); } freeaddrinfo(res); } #else /* !HAVE_IPv6_FUNCS */ hent = gethostbyname(hn); if (hent != NULL) { hn = hent->h_name; } #endif /* !HAVE_IPv6_FUNCS */ #endif /* HAVE_SOCKETS */ hostname = smalloc(strlen(hn) + 1); strcpy(hostname, hn); } return hostname; } /* * Return the user's From: address(es). */ char * myaddrs(struct header *hp) { char *cp, *hn; static char *addr; size_t sz; if (hp != NULL && hp->h_from != NULL) { if (hp->h_from->n_fullname) return savestr(hp->h_from->n_fullname); if (hp->h_from->n_name) return savestr(hp->h_from->n_name); } if ((cp = value("from")) != NULL) return cp; /* * When invoking sendmail directly, it's its task * to generate a From: address. */ if (value("smtp") == NULL) return NULL; if (addr == NULL) { hn = nodename(1); sz = strlen(myname) + strlen(hn) + 2; addr = smalloc(sz); snprintf(addr, sz, "%s@%s", myname, hn); } return addr; } char * myorigin(struct header *hp) { char *cp; struct name *np; if ((cp = myaddrs(hp)) == NULL || (np = sextract(cp, GEXTRA|GFULL)) == NULL) return NULL; return np->n_flink != NULL ? value("sender") : cp; } #ifdef HAVE_SOCKETS static int read_smtp(struct sock *sp, int value, int ign_eof); static int talk_smtp(struct name *to, FILE *fi, struct sock *sp, char *server, char *uhp, struct header *hp, const char *user, const char *password, const char *skinned); char * smtp_auth_var(const char *type, const char *addr) { char *var, *cp; int len; var = ac_alloc(len = strlen(type) + strlen(addr) + 7); snprintf(var, len, "smtp-auth%s-%s", type, addr); if ((cp = value(var)) != NULL) cp = savestr(cp); else { snprintf(var, len, "smtp-auth%s", type); if ((cp = value(var)) != NULL) cp = savestr(cp); } ac_free(var); return cp; } static char *smtpbuf; static size_t smtpbufsize; /* * Get the SMTP server's answer, expecting value. */ static int read_smtp(struct sock *sp, int value, int ign_eof) { int ret; int len; do { if ((len = sgetline(&smtpbuf, &smtpbufsize, NULL, sp)) < 6) { if (len >= 0 && !ign_eof) fprintf(stderr, catgets(catd, CATSET, 241, "Unexpected EOF on SMTP connection\n")); return -1; } if (verbose || debug || _debug) fputs(smtpbuf, stderr); switch (*smtpbuf) { case '1': ret = 1; break; case '2': ret = 2; break; case '3': ret = 3; break; case '4': ret = 4; break; default: ret = 5; } if (value != ret) fprintf(stderr, catgets(catd, CATSET, 191, "smtp-server: %s"), smtpbuf); } while (smtpbuf[3] == '-'); return ret; } /* * Macros for talk_smtp. */ #define _SMTP_ANSWER(x, ign_eof) \ if (!debug && !_debug) { \ int y; \ if ((y = read_smtp(sp, x, ign_eof)) != (x) && \ (!(ign_eof) || y != -1)) { \ if (b != NULL) \ free(b); \ return 1; \ } \ } #define SMTP_ANSWER(x) _SMTP_ANSWER(x, 0) #define SMTP_OUT(x) if (verbose || debug || _debug) \ fprintf(stderr, ">>> %s", x); \ if (!debug && !_debug) \ swrite(sp, x); /* * Talk to a SMTP server. */ static int talk_smtp(struct name *to, FILE *fi, struct sock *sp, char *xserver, char *uhp, struct header *hp, const char *user, const char *password, const char *skinned) { struct name *n; char *b = NULL, o[LINESIZE]; size_t blen, bsize = 0, count; char *b64, *authstr, *cp; enum { AUTH_NONE, AUTH_PLAIN, AUTH_LOGIN, AUTH_CRAM_MD5 } auth; int inhdr = 1, inbcc = 0; if ((authstr = smtp_auth_var("", skinned)) == NULL) auth = user && password ? AUTH_LOGIN : AUTH_NONE; else if (strcmp(authstr, "plain") == 0) auth = AUTH_PLAIN; else if (strcmp(authstr, "login") == 0) auth = AUTH_LOGIN; else if (strcmp(authstr, "cram-md5") == 0) auth = AUTH_CRAM_MD5; else { fprintf(stderr, "Unknown SMTP authentication " "method: \"%s\"\n", authstr); return 1; } if (auth != AUTH_NONE && (user == NULL || password == NULL)) { fprintf(stderr, "User and password are necessary " "for SMTP authentication.\n"); return 1; } SMTP_ANSWER(2); #ifdef USE_SSL if (value("smtp-use-starttls") || value("smtp-use-tls") /* v11.0 compatibility */) { char *server; if ((cp = strchr(xserver, ':')) != NULL) { server = salloc(cp - xserver + 1); memcpy(server, xserver, cp - xserver); server[cp - xserver] = '\0'; } else server = xserver; snprintf(o, sizeof o, "EHLO %s\r\n", nodename(1)); SMTP_OUT(o); SMTP_ANSWER(2); SMTP_OUT("STARTTLS\r\n"); SMTP_ANSWER(2); if (!debug && !_debug && ssl_open(server, sp, uhp) != OKAY) return 1; } #else /* !USE_SSL */ if (value("smtp-use-starttls") || value("smtp-use-tls")) { fprintf(stderr, "No SSL support compiled in.\n"); return 1; } #endif /* !USE_SSL */ if (auth != AUTH_NONE) { snprintf(o, sizeof o, "EHLO %s\r\n", nodename(1)); SMTP_OUT(o); SMTP_ANSWER(2); switch (auth) { default: case AUTH_LOGIN: SMTP_OUT("AUTH LOGIN\r\n"); SMTP_ANSWER(3); b64 = strtob64(user); snprintf(o, sizeof o, "%s\r\n", b64); free(b64); SMTP_OUT(o); SMTP_ANSWER(3); b64 = strtob64(password); snprintf(o, sizeof o, "%s\r\n", b64); free(b64); SMTP_OUT(o); SMTP_ANSWER(2); break; case AUTH_PLAIN: SMTP_OUT("AUTH PLAIN\r\n"); SMTP_ANSWER(3); snprintf(o, sizeof o, "%c%s%c%s", '\0', user, '\0', password); b64 = memtob64(o, strlen(user)+strlen(password)+2); snprintf(o, sizeof o, "%s\r\n", b64); SMTP_OUT(o); SMTP_ANSWER(2); break; case AUTH_CRAM_MD5: SMTP_OUT("AUTH CRAM-MD5\r\n"); SMTP_ANSWER(3); for (cp = smtpbuf; digitchar(*cp&0377); cp++); while (blankchar(*cp&0377)) cp++; cp = cram_md5_string(user, password, cp); SMTP_OUT(cp); SMTP_ANSWER(2); break; } } else { snprintf(o, sizeof o, "HELO %s\r\n", nodename(1)); SMTP_OUT(o); SMTP_ANSWER(2); } snprintf(o, sizeof o, "MAIL FROM:<%s>\r\n", skinned); SMTP_OUT(o); SMTP_ANSWER(2); for (n = to; n != NULL; n = n->n_flink) { if ((n->n_type & GDEL) == 0) { snprintf(o, sizeof o, "RCPT TO:<%s>\r\n", skin(n->n_name)); SMTP_OUT(o); SMTP_ANSWER(2); } } SMTP_OUT("DATA\r\n"); SMTP_ANSWER(3); fflush(fi); rewind(fi); count = fsize(fi); while (fgetline(&b, &bsize, &count, &blen, fi, 1) != NULL) { if (inhdr) { if (*b == '\n') { inhdr = 0; inbcc = 0; } else if (inbcc && blankchar(*b & 0377)) continue; /* * We know what we have generated first, so * do not look for whitespace before the ':'. */ else if (ascncasecmp(b, "bcc: ", 5) == 0) { inbcc = 1; continue; } else inbcc = 0; } if (*b == '.') { if (debug || _debug) putc('.', stderr); else swrite1(sp, ".", 1, 1); } if (debug || _debug) { fprintf(stderr, ">>> %s", b); continue; } b[blen-1] = '\r'; b[blen] = '\n'; swrite1(sp, b, blen+1, 1); } SMTP_OUT(".\r\n"); SMTP_ANSWER(2); SMTP_OUT("QUIT\r\n"); _SMTP_ANSWER(2, 1); if (b != NULL) free(b); return 0; } static sigjmp_buf smtpjmp; static void onterm(int signo) { siglongjmp(smtpjmp, 1); } /* * Connect to a SMTP server. */ int smtp_mta(char *server, struct name *to, FILE *fi, struct header *hp, const char *user, const char *password, const char *skinned) { struct sock so; int use_ssl, ret; sighandler_type saveterm; memset(&so, 0, sizeof so); verbose = value("verbose") != NULL; _debug = value("debug") != NULL; saveterm = safe_signal(SIGTERM, SIG_IGN); if (sigsetjmp(smtpjmp, 1)) { safe_signal(SIGTERM, saveterm); return 1; } if (saveterm != SIG_IGN) safe_signal(SIGTERM, onterm); if (strncmp(server, "smtp://", 7) == 0) { use_ssl = 0; server += 7; #ifdef USE_SSL } else if (strncmp(server, "smtps://", 8) == 0) { use_ssl = 1; server += 8; #endif } else use_ssl = 0; if (!debug && !_debug && sopen(server, &so, use_ssl, server, use_ssl ? "smtps" : "smtp", verbose) != OKAY) { safe_signal(SIGTERM, saveterm); return 1; } so.s_desc = "SMTP"; ret = talk_smtp(to, fi, &so, server, server, hp, user, password, skinned); if (!debug && !_debug) sclose(&so); if (smtpbuf) { free(smtpbuf); smtpbuf = NULL; smtpbufsize = 0; } safe_signal(SIGTERM, saveterm); return ret; } #else /* !HAVE_SOCKETS */ int smtp_mta(char *server, struct name *to, FILE *fi, struct header *hp, const char *user, const char *password, const char *skinned) { fputs(catgets(catd, CATSET, 194, "No SMTP support compiled in.\n"), stderr); return 1; } #endif /* !HAVE_SOCKETS */ heirloom-mailx-12.5/ssl.c000066400000000000000000000224141155563371200153470ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 2002 * Gunnar Ritter. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Gunnar Ritter * and his contributors. * 4. Neither the name of Gunnar Ritter nor the names of his contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)ssl.c 1.39 (gritter) 6/12/06"; #endif #endif /* not lint */ #include "config.h" #ifdef USE_SSL #include "rcv.h" #include "extern.h" void ssl_set_vrfy_level(const char *uhp) { char *cp; char *vrvar; ssl_vrfy_level = VRFY_ASK; vrvar = ac_alloc(strlen(uhp) + 12); strcpy(vrvar, "ssl-verify-"); strcpy(&vrvar[11], uhp); if ((cp = value(vrvar)) == NULL) cp = value("ssl-verify"); ac_free(vrvar); if (cp != NULL) { if (equal(cp, "strict")) ssl_vrfy_level = VRFY_STRICT; else if (equal(cp, "ask")) ssl_vrfy_level = VRFY_ASK; else if (equal(cp, "warn")) ssl_vrfy_level = VRFY_WARN; else if (equal(cp, "ignore")) ssl_vrfy_level = VRFY_IGNORE; else fprintf(stderr, catgets(catd, CATSET, 265, "invalid value of ssl-verify: %s\n"), cp); } } enum okay ssl_vrfy_decide(void) { enum okay ok = STOP; switch (ssl_vrfy_level) { case VRFY_STRICT: ok = STOP; break; case VRFY_ASK: { char *line = NULL; size_t linesize = 0; fprintf(stderr, catgets(catd, CATSET, 264, "Continue (y/n)? ")); if (readline(stdin, &line, &linesize) > 0 && *line == 'y') ok = OKAY; else ok = STOP; if (line) free(line); } break; case VRFY_WARN: case VRFY_IGNORE: ok = OKAY; } return ok; } char * ssl_method_string(const char *uhp) { char *cp, *mtvar; mtvar = ac_alloc(strlen(uhp) + 12); strcpy(mtvar, "ssl-method-"); strcpy(&mtvar[11], uhp); if ((cp = value(mtvar)) == NULL) cp = value("ssl-method"); ac_free(mtvar); return cp; } enum okay smime_split(FILE *ip, FILE **hp, FILE **bp, long xcount, int keep) { char *buf, *hn, *bn; char *savedfields = NULL; size_t bufsize, buflen, count, savedsize = 0; int c; if ((*hp = Ftemp(&hn, "Rh", "w+", 0600, 1)) == NULL || (*bp = Ftemp(&bn, "Rb", "w+", 0600, 1)) == NULL) { perror("tempfile"); return STOP; } rm(hn); rm(bn); Ftfree(&hn); Ftfree(&bn); buf = smalloc(bufsize = LINESIZE); savedfields = smalloc(savedsize = 1); *savedfields = '\0'; if (xcount < 0) count = fsize(ip); else count = xcount; while (fgetline(&buf, &bufsize, &count, &buflen, ip, 0) != NULL && *buf != '\n') { if (ascncasecmp(buf, "content-", 8) == 0) { if (keep) fputs("X-Encoded-", *hp); for (;;) { savedsize += buflen; savedfields = srealloc(savedfields, savedsize); strcat(savedfields, buf); if (keep) fwrite(buf, sizeof *buf, buflen, *hp); c = getc(ip); ungetc(c, ip); if (!blankchar(c)) break; fgetline(&buf, &bufsize, &count, &buflen, ip, 0); } continue; } fwrite(buf, sizeof *buf, buflen, *hp); } fflush(*hp); rewind(*hp); fputs(savedfields, *bp); putc('\n', *bp); while (fgetline(&buf, &bufsize, &count, &buflen, ip, 0) != NULL) fwrite(buf, sizeof *buf, buflen, *bp); fflush(*bp); rewind(*bp); free(buf); return OKAY; } FILE * smime_sign_assemble(FILE *hp, FILE *bp, FILE *sp) { char *boundary, *cp; FILE *op; int c, lastc = EOF; if ((op = Ftemp(&cp, "Rs", "w+", 0600, 1)) == NULL) { perror("tempfile"); return NULL; } rm(cp); Ftfree(&cp); boundary = makeboundary(); while ((c = getc(hp)) != EOF) { if (c == '\n' && lastc == '\n') break; putc(c, op); lastc = c; } fprintf(op, "Content-Type: multipart/signed;\n" " protocol=\"application/x-pkcs7-signature\"; micalg=sha1;\n" " boundary=\"%s\"\n\n", boundary); fprintf(op, "This is an S/MIME signed message.\n\n--%s\n", boundary); while ((c = getc(bp)) != EOF) putc(c, op); fprintf(op, "\n--%s\n", boundary); fputs("Content-Type: application/x-pkcs7-signature; " "name=\"smime.p7s\"\n" "Content-Transfer-Encoding: base64\n" "Content-Disposition: attachment; filename=\"smime.p7s\"\n\n", op); while ((c = getc(sp)) != EOF) { if (c == '-') { while ((c = getc(sp)) != EOF && c != '\n'); continue; } putc(c, op); } fprintf(op, "\n--%s--\n", boundary); Fclose(hp); Fclose(bp); Fclose(sp); fflush(op); if (ferror(op)) { perror("signed output data"); Fclose(op); return NULL; } rewind(op); return op; } FILE * smime_encrypt_assemble(FILE *hp, FILE *yp) { char *cp; FILE *op; int c, lastc = EOF; if ((op = Ftemp(&cp, "Rs", "w+", 0600, 1)) == NULL) { perror("tempfile"); return NULL; } rm(cp); Ftfree(&cp); while ((c = getc(hp)) != EOF) { if (c == '\n' && lastc == '\n') break; putc(c, op); lastc = c; } fprintf(op, "Content-Type: application/x-pkcs7-mime; " "name=\"smime.p7m\"\n" "Content-Transfer-Encoding: base64\n" "Content-Disposition: attachment; " "filename=\"smime.p7m\"\n\n"); while ((c = getc(yp)) != EOF) { if (c == '-') { while ((c = getc(yp)) != EOF && c != '\n'); continue; } putc(c, op); } Fclose(hp); Fclose(yp); fflush(op); if (ferror(op)) { perror("encrypted output data"); Fclose(op); return NULL; } rewind(op); return op; } struct message * smime_decrypt_assemble(struct message *m, FILE *hp, FILE *bp) { int binary = 0, lastnl = 0; char *buf = NULL, *cp; size_t bufsize = 0, buflen, count; long lines = 0, octets = 0; struct message *x; off_t offset; x = salloc(sizeof *x); *x = *m; fflush(mb.mb_otf); fseek(mb.mb_otf, 0L, SEEK_END); offset = ftell(mb.mb_otf); count = fsize(hp); while (fgetline(&buf, &bufsize, &count, &buflen, hp, 0) != NULL) { if (buf[0] == '\n') break; if ((cp = thisfield(buf, "content-transfer-encoding")) != NULL) if (ascncasecmp(cp, "binary", 7) == 0) binary = 1; fwrite(buf, sizeof *buf, buflen, mb.mb_otf); octets += buflen; lines++; } octets += mkdate(mb.mb_otf, "X-Decoding-Date"); lines++; count = fsize(bp); while (fgetline(&buf, &bufsize, &count, &buflen, bp, 0) != NULL) { lines++; if (!binary && buf[buflen-1] == '\n' && buf[buflen-2] == '\r') buf[--buflen-1] = '\n'; fwrite(buf, sizeof *buf, buflen, mb.mb_otf); octets += buflen; if (buf[0] == '\n') lastnl++; else if (buf[buflen-1] == '\n') lastnl = 1; else lastnl = 0; } while (!binary && lastnl < 2) { putc('\n', mb.mb_otf); lines++; octets++; lastnl++; } Fclose(hp); Fclose(bp); free(buf); fflush(mb.mb_otf); if (ferror(mb.mb_otf)) { perror("decrypted output data"); return NULL; } x->m_size = x->m_xsize = octets; x->m_lines = x->m_xlines = lines; x->m_block = mailx_blockof(offset); x->m_offset = mailx_offsetof(offset); return x; } int ccertsave(void *v) { int *ip; int f, *msgvec; char *file = NULL, *str = v; int val = 0; FILE *fp; msgvec = salloc((msgCount + 2) * sizeof *msgvec); if ((file = laststring(str, &f, 1)) == NULL) { fprintf(stderr, "No file to save certificate given.\n"); return 1; } if (!f) { *msgvec = first(0, MMNORM); if (*msgvec == 0) { if (inhook) return 0; fprintf(stderr, "No messages to get certificates from.\n"); return 1; } msgvec[1] = 0; } else if (getmsglist(str, msgvec, 0) < 0) return 1; if (*msgvec == 0) { if (inhook) return 0; fprintf(stderr, "No applicable messages.\n"); return 1; } if ((fp = Fopen(file, "a")) == NULL) { perror(file); return 1; } for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) if (smime_certsave(&message[*ip-1], *ip, fp) != OKAY) val = 1; Fclose(fp); if (val == 0) printf("Certificate(s) saved.\n"); return val; } enum okay rfc2595_hostname_match(const char *host, const char *pattern) { if (pattern[0] == '*' && pattern[1] == '.') { pattern++; while (*host && *host != '.') host++; } return asccasecmp(host, pattern) == 0 ? OKAY : STOP; } #endif /* USE_SSL */ heirloom-mailx-12.5/strings.c000066400000000000000000000100761155563371200162400ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)strings.c 2.6 (gritter) 3/4/06"; #endif #endif /* not lint */ /* * Mail -- a mail program * * String allocation routines. * Strings handed out here are reclaimed at the top of the command * loop each time, so they need not be freed. */ #include "rcv.h" #include "extern.h" /* * Allocate size more bytes of space and return the address of the * first byte to the caller. An even number of bytes are always * allocated so that the space will always be on a word boundary. * The string spaces are of exponentially increasing size, to satisfy * the occasional user with enormous string size requests. */ void * salloc(size_t size) { char *t; int s; struct strings *sp; int string_index; s = size; s += (sizeof (char *) - 1); s &= ~(sizeof (char *) - 1); string_index = 0; for (sp = &stringdope[0]; sp < &stringdope[NSPACE]; sp++) { if (sp->s_topFree == NULL && (STRINGSIZE << string_index) >= s) break; if (sp->s_nleft >= s) break; string_index++; } if (sp >= &stringdope[NSPACE]) panic(catgets(catd, CATSET, 195, "String too large")); if (sp->s_topFree == NULL) { string_index = sp - &stringdope[0]; sp->s_topFree = smalloc(STRINGSIZE << string_index); sp->s_nextFree = sp->s_topFree; sp->s_nleft = STRINGSIZE << string_index; } sp->s_nleft -= s; t = sp->s_nextFree; sp->s_nextFree += s; return(t); } void * csalloc(size_t nmemb, size_t size) { void *vp; vp = salloc(nmemb * size); memset(vp, 0, nmemb * size); return vp; } /* * Reset the string area to be empty. * Called to free all strings allocated * since last reset. */ void sreset(void) { struct strings *sp; int string_index; if (noreset) return; string_index = 0; for (sp = &stringdope[0]; sp < &stringdope[NSPACE]; sp++) { if (sp->s_topFree == NULL) continue; sp->s_nextFree = sp->s_topFree; sp->s_nleft = STRINGSIZE << string_index; string_index++; } } /* * Make the string area permanent. * Meant to be called in main, after initialization. */ void spreserve(void) { struct strings *sp; for (sp = &stringdope[0]; sp < &stringdope[NSPACE]; sp++) sp->s_topFree = NULL; } heirloom-mailx-12.5/temp.c000066400000000000000000000102471155563371200155140ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)temp.c 2.8 (gritter) 3/4/06"; #endif #endif /* not lint */ #include "rcv.h" #include "extern.h" #include #include #include /* * Mail -- a mail program * * Temporary file handling. */ static char *tmpdir; /* * Create a temporary file in tmpdir, use prefix for its name, * store the unique name in fn, and return a stdio FILE pointer * with access mode. * The permissions for the newly created file are given in bits. */ FILE * Ftemp(char **fn, char *prefix, char *mode, int bits, int register_file) { FILE *fp; int fd; *fn = smalloc(strlen(tmpdir) + strlen(prefix) + 8); strcpy(*fn, tmpdir); strcat(*fn, "/"); strcat(*fn, prefix); strcat(*fn, "XXXXXX"); #ifdef HAVE_MKSTEMP if ((fd = mkstemp(*fn)) < 0) goto Ftemperr; if (fchmod(fd, bits) < 0) goto Ftemperr; #else /* !HAVE_MKSTEMP */ if (mktemp(*fn) == NULL) goto Ftemperr; if ((fd = open(*fn, O_CREAT|O_EXCL|O_RDWR, bits)) < 0) goto Ftemperr; #endif /* !HAVE_MKSTEMP */ if (register_file) fp = Fdopen(fd, mode); else { fp = fdopen(fd, mode); fcntl(fd, F_SETFD, FD_CLOEXEC); } return fp; Ftemperr: Ftfree(fn); return NULL; } /* * Free the resources associated with the given filename. To be * called after unlink(). * Since this function can be called after receiving a signal, * the variable must be made NULL first and then free()d, to avoid * more than one free() call in all circumstances. */ void Ftfree(char **fn) { char *cp = *fn; *fn = NULL; free(cp); } void tinit(void) { char *cp; if ((cp = getenv("TMPDIR")) != NULL) { tmpdir = smalloc(strlen(cp) + 1); strcpy(tmpdir, cp); } else { tmpdir = "/tmp"; } if (myname != NULL) { if (getuserid(myname) < 0) { printf(catgets(catd, CATSET, 198, "\"%s\" is not a user of this system\n"), myname); exit(1); } } else { if ((cp = username()) == NULL) { myname = "nobody"; if (rcvmode) exit(1); } else { myname = smalloc(strlen(cp) + 1); strcpy(myname, cp); } } if ((cp = getenv("HOME")) == NULL) cp = "."; homedir = smalloc(strlen(cp) + 1); strcpy(homedir, cp); if (debug || value("debug")) printf(catgets(catd, CATSET, 199, "user = %s, homedir = %s\n"), myname, homedir); } heirloom-mailx-12.5/thread.c000066400000000000000000000440161155563371200160170ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 2004 * Gunnar Ritter. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Gunnar Ritter * and his contributors. * 4. Neither the name of Gunnar Ritter nor the names of his contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)thread.c 1.57 (gritter) 3/4/06"; #endif #endif /* not lint */ #include "config.h" #include "rcv.h" #include "extern.h" #include /* * Mail -- a mail program * * Message threading. */ /* * Open addressing is used for Message-IDs because the maximum number of * messages in the table is known in advance (== msgCount). */ struct mitem { struct message *mi_data; char *mi_id; }; struct msort { union { long ms_long; char *ms_char; float ms_float; } ms_u; int ms_n; }; static unsigned mhash(const char *cp, int mprime); static struct mitem *mlook(char *id, struct mitem *mt, struct message *mdata, int mprime); static void adopt(struct message *parent, struct message *child, int dist); static struct message *interlink(struct message *m, long count, int newmail); static void finalize(struct message *mp); static int mlonglt(const void *a, const void *b); static int mfloatlt(const void *a, const void *b); static int mcharlt(const void *a, const void *b); static void lookup(struct message *m, struct mitem *mi, int mprime); static void makethreads(struct message *m, long count, int newmail); static char *skipre(const char *cp); static int colpt(int *msgvec, int cl); static void colps(struct message *b, int cl); static void colpm(struct message *m, int cl, int *cc, int *uc); /* * Return the hash value for a message id modulo mprime, or mprime * if the passed string does not look like a message-id. */ static unsigned mhash(const char *cp, int mprime) { unsigned h = 0, g, at = 0; cp--; while (*++cp) { /* * Pay attention not to hash characters which are * irrelevant for Message-ID semantics. */ if (*cp == '(') { cp = skip_comment(&cp[1]) - 1; continue; } if (*cp == '"' || *cp == '\\') continue; if (*cp == '@') at++; h = ((h << 4) & 0xffffffff) + lowerconv(*cp & 0377); if ((g = h & 0xf0000000) != 0) { h = h ^ (g >> 24); h = h ^ g; } } return at ? h % mprime : mprime; } #define NOT_AN_ID ((struct mitem *)-1) /* * Look up a message id. Returns NOT_AN_ID if the passed string does * not look like a message-id. */ static struct mitem * mlook(char *id, struct mitem *mt, struct message *mdata, int mprime) { struct mitem *mp; unsigned h, c, n = 0; if (id == NULL && (id = hfield("message-id", mdata)) == NULL) return NULL; if (mdata && mdata->m_idhash) h = ~mdata->m_idhash; else { h = mhash(id, mprime); if (h == mprime) return NOT_AN_ID; } mp = &mt[c = h]; while (mp->mi_id != NULL) { if (msgidcmp(mp->mi_id, id) == 0) break; c += n&1 ? -((n+1)/2) * ((n+1)/2) : ((n+1)/2) * ((n+1)/2); n++; while (c >= mprime) c -= mprime; mp = &mt[c]; } if (mdata != NULL && mp->mi_id == NULL) { mp->mi_id = id; mp->mi_data = mdata; mdata->m_idhash = ~h; } return mp->mi_id ? mp : NULL; } /* * Child is to be adopted by parent. A thread tree is structured * as follows: * * ------ m_child ------ m_child * | |-------------------->| |------------------------> . . . * | |<--------------------| |<----------------------- . . . * ------ m_parent ------ m_parent * ^^ | ^ * | \____ m_younger | | * | \ | | * | ---- | | * | \ | | m_elder * | m_parent ---- | | * | \ | | * | ---- | | * | \ + | * | ------ m_child * | | |------------------------> . . . * | | |<----------------------- . . . * | ------ m_parent * | | ^ * \----- m_younger | | * \ | | * ---- | | * \ | | m_elder * m_parent ---- | | * \ | | * ---- | | * \ + | * ------ m_child * | |------------------------> . . . * | |<----------------------- . . . * ------ m_parent * | ^ * . . . * * The base message of a thread does not have a m_parent link. Elements * connected by m_younger/m_elder links are replies to the same message, * which is connected to them by m_parent links. The first reply to a * message gets the m_child link. */ static void adopt(struct message *parent, struct message *child, int dist) { struct message *mp, *mq; for (mp = parent; mp; mp = mp->m_parent) if (mp == child) return; child->m_level = dist; /* temporarily store distance */ child->m_parent = parent; if (parent->m_child != NULL) { mq = NULL; for (mp = parent->m_child; mp; mp = mp->m_younger) { if (mp->m_date >= child->m_date) { if (mp->m_elder) mp->m_elder->m_younger = child; child->m_elder = mp->m_elder; mp->m_elder = child; child->m_younger = mp; if (mp == parent->m_child) parent->m_child = child; return; } mq = mp; } mq->m_younger = child; child->m_elder = mq; } else parent->m_child = child; } /* * Connect all messages on the lowest thread level with m_younger/m_elder * links. */ static struct message * interlink(struct message *m, long count, int newmail) { int i; long n; struct msort *ms; struct message *root; int autocollapse = !newmail && !(inhook&2) && value("autocollapse") != NULL; ms = smalloc(sizeof *ms * count); for (n = 0, i = 0; i < count; i++) { if (m[i].m_parent == NULL) { if (autocollapse) colps(&m[i], 1); ms[n].ms_u.ms_long = m[i].m_date; ms[n].ms_n = i; n++; } } if (n > 0) { qsort(ms, n, sizeof *ms, mlonglt); root = &m[ms[0].ms_n]; for (i = 1; i < n; i++) { m[ms[i-1].ms_n].m_younger = &m[ms[i].ms_n]; m[ms[i].ms_n].m_elder = &m[ms[i-1].ms_n]; } } else root = &m[0]; free(ms); return root; } static void finalize(struct message *mp) { long n; for (n = 0; mp; mp = next_in_thread(mp)) { mp->m_threadpos = ++n; mp->m_level = mp->m_parent ? mp->m_level + mp->m_parent->m_level : 0; } } static int mlonglt(const void *a, const void *b) { int i; i = ((struct msort *)a)->ms_u.ms_long - ((struct msort *)b)->ms_u.ms_long; if (i == 0) i = ((struct msort *)a)->ms_n - ((struct msort *)b)->ms_n; return i; } static int mfloatlt(const void *a, const void *b) { float i; i = ((struct msort *)a)->ms_u.ms_float - ((struct msort *)b)->ms_u.ms_float; if (i == 0) i = ((struct msort *)a)->ms_n - ((struct msort *)b)->ms_n; return i > 0 ? 1 : i < 0 ? -1 : 0; } static int mcharlt(const void *a, const void *b) { int i; i = strcoll(((struct msort *)a)->ms_u.ms_char, ((struct msort *)b)->ms_u.ms_char); if (i == 0) i = ((struct msort *)a)->ms_n - ((struct msort *)b)->ms_n; return i; } static void lookup(struct message *m, struct mitem *mi, int mprime) { struct name *np; struct mitem *ip; char *cp; long dist; if (m->m_flag & MHIDDEN) return; dist = 1; if ((cp = hfield("in-reply-to", m)) != NULL) { if ((np = extract(cp, GREF)) != NULL) do { if ((ip = mlook(np->n_name, mi, NULL, mprime)) != NULL && ip != NOT_AN_ID) { adopt(ip->mi_data, m, 1); return; } } while ((np = np->n_flink) != NULL); } if ((cp = hfield("references", m)) != NULL) { if ((np = extract(cp, GREF)) != NULL) { while (np->n_flink != NULL) np = np->n_flink; do { if ((ip = mlook(np->n_name, mi, NULL, mprime)) != NULL) { if (ip == NOT_AN_ID) continue; /* skip dist++ */ adopt(ip->mi_data, m, dist); return; } dist++; } while ((np = np->n_blink) != NULL); } } } static void makethreads(struct message *m, long count, int newmail) { struct mitem *mt; char *cp; long i, mprime; if (count == 0) return; mprime = nextprime(count); mt = scalloc(mprime, sizeof *mt); for (i = 0; i < count; i++) { if ((m[i].m_flag&MHIDDEN) == 0) { mlook(NULL, mt, &m[i], mprime); if (m[i].m_date == 0) { if ((cp = hfield("date", &m[i])) != NULL) m[i].m_date = rfctime(cp); } } m[i].m_child = m[i].m_younger = m[i].m_elder = m[i].m_parent = NULL; m[i].m_level = 0; if (!newmail && !(inhook&2)) m[i].m_collapsed = 0; } /* * Most folders contain the eldest messages first. Traversing * them in descending order makes it more likely that younger * brothers are found first, so elder ones can be prepended to * the brother list, which is faster. The worst case is still * in O(n^2) and occurs when all but one messages in a folder * are replies to the one message, and are sorted such that * youngest messages occur first. */ for (i = count-1; i >= 0; i--) lookup(&m[i], mt, mprime); threadroot = interlink(m, count, newmail); finalize(threadroot); free(mt); mb.mb_threaded = 1; } int thread(void *vp) { if (mb.mb_threaded != 1 || vp == NULL || vp == (void *)-1) { if (mb.mb_type == MB_IMAP) imap_getheaders(1, msgCount); makethreads(message, msgCount, vp == (void *)-1); free(mb.mb_sorted); mb.mb_sorted = sstrdup("thread"); } if (vp && vp != (void *)-1 && !inhook && value("header")) return headers(vp); return 0; } int unthread(void *vp) { struct message *m; mb.mb_threaded = 0; free(mb.mb_sorted); mb.mb_sorted = NULL; for (m = &message[0]; m < &message[msgCount]; m++) m->m_collapsed = 0; if (vp && !inhook && value("header")) return headers(vp); return 0; } struct message * next_in_thread(struct message *mp) { if (mp->m_child) return mp->m_child; if (mp->m_younger) return mp->m_younger; while (mp->m_parent) { if (mp->m_parent->m_younger) return mp->m_parent->m_younger; mp = mp->m_parent; } return NULL; } struct message * prev_in_thread(struct message *mp) { if (mp->m_elder) { mp = mp->m_elder; while (mp->m_child) { mp = mp->m_child; while (mp->m_younger) mp = mp->m_younger; } return mp; } return mp->m_parent; } struct message * this_in_thread(struct message *mp, long n) { struct message *mq; if (n == -1) { /* find end of thread */ while (mp) { if (mp->m_younger) { mp = mp->m_younger; continue; } mq = next_in_thread(mp); if (mq == NULL || mq->m_threadpos < mp->m_threadpos) return mp; mp = mq; } return NULL; } while (mp && mp->m_threadpos < n) { if (mp->m_younger && mp->m_younger->m_threadpos <= n) { mp = mp->m_younger; continue; } mp = next_in_thread(mp); } return mp && mp->m_threadpos == n ? mp : NULL; } /* * Sorted mode is internally just a variant of threaded mode with all * m_parent and m_child links being NULL. */ int sort(void *vp) { enum method { SORT_SUBJECT, SORT_DATE, SORT_STATUS, SORT_SIZE, SORT_FROM, SORT_TO, SORT_SCORE, SORT_THREAD } method; struct { const char *me_name; enum method me_method; int (*me_func)(const void *, const void *); } methnames[] = { { "date", SORT_DATE, mlonglt }, { "from", SORT_FROM, mcharlt }, { "to", SORT_TO, mcharlt }, { "subject", SORT_SUBJECT, mcharlt }, { "size", SORT_SIZE, mlonglt }, { "status", SORT_STATUS, mlonglt }, { "score", SORT_SCORE, mfloatlt }, { "thread", SORT_THREAD, NULL }, { NULL, -1, NULL } }; char **args = (char **)vp, *cp, *_args[2]; int (*func)(const void *, const void *); struct msort *ms; struct str in, out; int i, n, msgvec[2]; int showname = value("showname") != NULL; struct message *mp; msgvec[0] = dot - &message[0] + 1; msgvec[1] = 0; if (vp == NULL || vp == (void *)-1) { _args[0] = savestr(mb.mb_sorted); _args[1] = NULL; args = _args; } else if (args[0] == NULL) { printf("Current sorting criterion is: %s\n", mb.mb_sorted ? mb.mb_sorted : "unsorted"); return 0; } for (i = 0; methnames[i].me_name; i++) if (*args[0] && is_prefix(args[0], methnames[i].me_name)) break; if (methnames[i].me_name == NULL) { fprintf(stderr, "Unknown sorting method \"%s\"\n", args[0]); return 1; } method = methnames[i].me_method; func = methnames[i].me_func; free(mb.mb_sorted); mb.mb_sorted = sstrdup(args[0]); if (method == SORT_THREAD) return thread(vp && vp != (void *)-1 ? msgvec : vp); ms = ac_alloc(sizeof *ms * msgCount); switch (method) { case SORT_SUBJECT: case SORT_DATE: case SORT_FROM: case SORT_TO: if (mb.mb_type == MB_IMAP) imap_getheaders(1, msgCount); break; default: break; } for (n = 0, i = 0; i < msgCount; i++) { mp = &message[i]; if ((mp->m_flag&MHIDDEN) == 0) { switch (method) { case SORT_DATE: if (mp->m_date == 0 && (cp = hfield("date", mp)) != 0) mp->m_date = rfctime(cp); ms[n].ms_u.ms_long = mp->m_date; break; case SORT_STATUS: if (mp->m_flag & MDELETED) ms[n].ms_u.ms_long = 1; else if ((mp->m_flag&(MNEW|MREAD)) == MNEW) ms[n].ms_u.ms_long = 90; else if (mp->m_flag & MFLAGGED) ms[n].ms_u.ms_long = 85; else if ((mp->m_flag&(MNEW|MBOX)) == MBOX) ms[n].ms_u.ms_long = 70; else if (mp->m_flag & MNEW) ms[n].ms_u.ms_long = 80; else if (mp->m_flag & MREAD) ms[n].ms_u.ms_long = 40; else ms[n].ms_u.ms_long = 60; break; case SORT_SIZE: ms[n].ms_u.ms_long = mp->m_xsize; break; case SORT_SCORE: ms[n].ms_u.ms_float = mp->m_score; break; case SORT_FROM: case SORT_TO: if ((cp = hfield(method == SORT_FROM ? "from" : "to", mp)) != NULL) { ms[n].ms_u.ms_char = showname ? realname(cp) : skin(cp); makelow(ms[n].ms_u.ms_char); } else ms[n].ms_u.ms_char = ""; break; default: case SORT_SUBJECT: if ((cp = hfield("subject", mp)) != NULL) { in.s = cp; in.l = strlen(in.s); mime_fromhdr(&in, &out, TD_ICONV); ms[n].ms_u.ms_char = savestr(skipre(out.s)); free(out.s); makelow(ms[n].ms_u.ms_char); } else ms[n].ms_u.ms_char = ""; break; } ms[n++].ms_n = i; } mp->m_child = mp->m_younger = mp->m_elder = mp->m_parent = NULL; mp->m_level = 0; mp->m_collapsed = 0; } if (n > 0) { qsort(ms, n, sizeof *ms, func); threadroot = &message[ms[0].ms_n]; for (i = 1; i < n; i++) { message[ms[i-1].ms_n].m_younger = &message[ms[i].ms_n]; message[ms[i].ms_n].m_elder = &message[ms[i-1].ms_n]; } } else threadroot = &message[0]; finalize(threadroot); mb.mb_threaded = 2; ac_free(ms); return vp && vp != (void *)-1 && !inhook && value("header") ? headers(msgvec) : 0; } static char * skipre(const char *cp) { if (lowerconv(cp[0]&0377) == 'r' && lowerconv(cp[1]&0377) == 'e' && cp[2] == ':' && spacechar(cp[3]&0377)) { cp = &cp[4]; while (spacechar(*cp&0377)) cp++; } return (char *)cp; } int ccollapse(void *v) { return colpt(v, 1); } int cuncollapse(void *v) { return colpt(v, 0); } static int colpt(int *msgvec, int cl) { int *ip; if (mb.mb_threaded != 1) { puts("Not in threaded mode."); return 1; } for (ip = msgvec; *ip != 0; ip++) colps(&message[*ip-1], cl); return 0; } static void colps(struct message *b, int cl) { struct message *m; int cc = 0, uc = 0; if (cl && (b->m_collapsed > 0 || (b->m_flag & (MNEW|MREAD)) == MNEW)) return; if (b->m_child) { m = b->m_child; colpm(m, cl, &cc, &uc); for (m = m->m_younger; m; m = m->m_younger) colpm(m, cl, &cc, &uc); } if (cl) { b->m_collapsed = -cc; for (m = b->m_parent; m; m = m->m_parent) if (m->m_collapsed <= -uc ) { m->m_collapsed += uc; break; } } else { if (b->m_collapsed > 0) { b->m_collapsed = 0; uc++; } for (m = b; m; m = m->m_parent) if (m->m_collapsed <= -uc) { m->m_collapsed += uc; break; } } } static void colpm(struct message *m, int cl, int *cc, int *uc) { if (cl) { if (m->m_collapsed > 0) (*uc)++; if ((m->m_flag & (MNEW|MREAD)) != MNEW || m->m_collapsed < 0) m->m_collapsed = 1; if (m->m_collapsed > 0) (*cc)++; } else { if (m->m_collapsed > 0) { m->m_collapsed = 0; (*uc)++; } } if (m->m_child) { m = m->m_child; colpm(m, cl, cc, uc); for (m = m->m_younger; m; m = m->m_younger) colpm(m, cl, cc, uc); } } void uncollapse1(struct message *m, int always) { if (mb.mb_threaded == 1 && (always || m->m_collapsed > 0)) colps(m, 0); } heirloom-mailx-12.5/tty.c000066400000000000000000000251441155563371200153710ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)tty.c 2.29 (gritter) 3/9/07"; #endif #endif /* not lint */ /* * Mail -- a mail program * * Generally useful tty stuff. */ #include "rcv.h" #include "extern.h" #include #include #include #include static cc_t c_erase; /* Current erase char */ static cc_t c_kill; /* Current kill char */ static sigjmp_buf rewrite; /* Place to go when continued */ static sigjmp_buf intjmp; /* Place to go when interrupted */ #ifndef TIOCSTI static int ttyset; /* We must now do erase/kill */ #endif static struct termios ttybuf; static long vdis; /* _POSIX_VDISABLE char */ static void ttystop(int s); static void ttyint(int s); static int safe_getc(FILE *ibuf); static char *rtty_internal(const char *pr, char *src); /* * Receipt continuation. */ static void ttystop(int s) { sighandler_type old_action = safe_signal(s, SIG_DFL); sigset_t nset; sigemptyset(&nset); sigaddset(&nset, s); sigprocmask(SIG_BLOCK, &nset, NULL); kill(0, s); sigprocmask(SIG_UNBLOCK, &nset, NULL); safe_signal(s, old_action); siglongjmp(rewrite, 1); } /*ARGSUSED*/ static void ttyint(int s) { siglongjmp(intjmp, 1); } /* * Interrupts will cause trouble if we are inside a stdio call. As * this is only relevant if input comes from a terminal, we can simply * bypass it by read() then. */ static int safe_getc(FILE *ibuf) { if (fileno(ibuf) == 0 && is_a_tty[0]) { char c; int sz; again: if ((sz = read(0, &c, 1)) != 1) { if (sz < 0 && errno == EINTR) goto again; return EOF; } return c & 0377; } else return getc(ibuf); } /* * Read up a header from standard input. * The source string has the preliminary contents to * be read. */ static char * rtty_internal(const char *pr, char *src) { char ch, canonb[LINESIZE]; int c; char *cp, *cp2; (void) &c; (void) &cp2; fputs(pr, stdout); fflush(stdout); if (src != NULL && strlen(src) > sizeof canonb - 2) { printf(catgets(catd, CATSET, 200, "too long to edit\n")); return(src); } #ifndef TIOCSTI if (src != NULL) cp = sstpcpy(canonb, src); else cp = sstpcpy(canonb, ""); fputs(canonb, stdout); fflush(stdout); #else cp = src == NULL ? "" : src; while ((c = *cp++) != '\0') { if ((c_erase != vdis && c == c_erase) || (c_kill != vdis && c == c_kill)) { ch = '\\'; ioctl(0, TIOCSTI, &ch); } ch = c; ioctl(0, TIOCSTI, &ch); } cp = canonb; *cp = 0; #endif cp2 = cp; while (cp2 < canonb + sizeof canonb) *cp2++ = 0; cp2 = cp; if (sigsetjmp(rewrite, 1)) goto redo; safe_signal(SIGTSTP, ttystop); safe_signal(SIGTTOU, ttystop); safe_signal(SIGTTIN, ttystop); clearerr(stdin); while (cp2 < canonb + sizeof canonb - 1) { c = safe_getc(stdin); if (c == EOF || c == '\n') break; *cp2++ = c; } *cp2 = 0; safe_signal(SIGTSTP, SIG_DFL); safe_signal(SIGTTOU, SIG_DFL); safe_signal(SIGTTIN, SIG_DFL); if (c == EOF && ferror(stdin)) { redo: cp = strlen(canonb) > 0 ? canonb : NULL; clearerr(stdin); return(rtty_internal(pr, cp)); } #ifndef TIOCSTI if (cp == NULL || *cp == '\0') return(src); cp2 = cp; if (!ttyset) return(strlen(canonb) > 0 ? savestr(canonb) : NULL); while (*cp != '\0') { c = *cp++; if (c_erase != vdis && c == c_erase) { if (cp2 == canonb) continue; if (cp2[-1] == '\\') { cp2[-1] = c; continue; } cp2--; continue; } if (c_kill != vdis && c == c_kill) { if (cp2 == canonb) continue; if (cp2[-1] == '\\') { cp2[-1] = c; continue; } cp2 = canonb; continue; } *cp2++ = c; } *cp2 = '\0'; #endif if (equal("", canonb)) return(NULL); return(savestr(canonb)); } /* * Read all relevant header fields. */ #ifndef TIOCSTI #define TTYSET_CHECK(h) if (!ttyset && (h) != NULL) \ ttyset++, tcsetattr(0, TCSADRAIN, \ &ttybuf); #else #define TTYSET_CHECK(h) #endif #define GRAB_SUBJECT if (gflags & GSUBJECT) { \ TTYSET_CHECK(hp->h_subject) \ hp->h_subject = rtty_internal("Subject: ", \ hp->h_subject); \ } static struct name * grabaddrs(const char *field, struct name *np, int comma, enum gfield gflags) { struct name *nq; TTYSET_CHECK(np); loop: np = sextract(rtty_internal(field, detract(np, comma)), gflags); for (nq = np; nq != NULL; nq = nq->n_flink) if (mime_name_invalid(nq->n_name, 1)) goto loop; return np; } int grabh(struct header *hp, enum gfield gflags, int subjfirst) { sighandler_type saveint; #ifndef TIOCSTI sighandler_type savequit; #endif sighandler_type savetstp; sighandler_type savettou; sighandler_type savettin; int errs; int comma; (void) , (void) &saveint; savetstp = safe_signal(SIGTSTP, SIG_DFL); savettou = safe_signal(SIGTTOU, SIG_DFL); savettin = safe_signal(SIGTTIN, SIG_DFL); errs = 0; comma = value("bsdcompat") || value("bsdmsgs") ? 0 : GCOMMA; #ifndef TIOCSTI ttyset = 0; #endif if (tcgetattr(fileno(stdin), &ttybuf) < 0) { perror("tcgetattr"); return(-1); } c_erase = ttybuf.c_cc[VERASE]; c_kill = ttybuf.c_cc[VKILL]; #if defined (_PC_VDISABLE) && defined (HAVE_FPATHCONF) if ((vdis = fpathconf(0, _PC_VDISABLE)) < 0) vdis = '\377'; #elif defined (_POSIX_VDISABLE) vdis = _POSIX_VDISABLE; #else vdis = '\377'; #endif #ifndef TIOCSTI ttybuf.c_cc[VERASE] = 0; ttybuf.c_cc[VKILL] = 0; if ((saveint = safe_signal(SIGINT, SIG_IGN)) == SIG_DFL) safe_signal(SIGINT, SIG_DFL); if ((savequit = safe_signal(SIGQUIT, SIG_IGN)) == SIG_DFL) safe_signal(SIGQUIT, SIG_DFL); #else /* TIOCSTI */ saveint = safe_signal(SIGINT, SIG_IGN); if (sigsetjmp(intjmp, 1)) { /* avoid garbled output with C-c */ printf("\n"); fflush(stdout); goto out; } if (saveint != SIG_IGN) safe_signal(SIGINT, ttyint); #endif /* TIOCSTI */ if (gflags & GTO) hp->h_to = grabaddrs("To: ", hp->h_to, comma, GTO|GFULL); if (subjfirst) GRAB_SUBJECT if (gflags & GCC) hp->h_cc = grabaddrs("Cc: ", hp->h_cc, comma, GCC|GFULL); if (gflags & GBCC) hp->h_bcc = grabaddrs("Bcc: ", hp->h_bcc, comma, GBCC|GFULL); if (gflags & GEXTRA) { if (hp->h_from == NULL) hp->h_from = sextract(myaddrs(hp), GEXTRA|GFULL); hp->h_from = grabaddrs("From: ", hp->h_from, comma, GEXTRA|GFULL); if (hp->h_replyto == NULL) hp->h_replyto = sextract(value("replyto"), GEXTRA|GFULL); hp->h_replyto = grabaddrs("Reply-To: ", hp->h_replyto, comma, GEXTRA|GFULL); if (hp->h_sender == NULL) hp->h_sender = sextract(value("sender"), GEXTRA|GFULL); hp->h_sender = grabaddrs("Sender: ", hp->h_sender, comma, GEXTRA|GFULL); if (hp->h_organization == NULL) hp->h_organization = value("ORGANIZATION"); TTYSET_CHECK(hp->h_organization); hp->h_organization = rtty_internal("Organization: ", hp->h_organization); } if (!subjfirst) GRAB_SUBJECT out: safe_signal(SIGTSTP, savetstp); safe_signal(SIGTTOU, savettou); safe_signal(SIGTTIN, savettin); #ifndef TIOCSTI ttybuf.c_cc[VERASE] = c_erase; ttybuf.c_cc[VKILL] = c_kill; if (ttyset) tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); safe_signal(SIGQUIT, savequit); #endif safe_signal(SIGINT, saveint); return(errs); } /* * Read a line from tty; to be called from elsewhere */ char * readtty(char *prefix, char *string) { char *ret = NULL; struct termios ttybuf; sighandler_type saveint = SIG_DFL; #ifndef TIOCSTI sighandler_type savequit; #endif sighandler_type savetstp; sighandler_type savettou; sighandler_type savettin; (void) &saveint; (void) &ret; savetstp = safe_signal(SIGTSTP, SIG_DFL); savettou = safe_signal(SIGTTOU, SIG_DFL); savettin = safe_signal(SIGTTIN, SIG_DFL); #ifndef TIOCSTI ttyset = 0; #endif if (tcgetattr(fileno(stdin), &ttybuf) < 0) { perror("tcgetattr"); return NULL; } c_erase = ttybuf.c_cc[VERASE]; c_kill = ttybuf.c_cc[VKILL]; #ifndef TIOCSTI ttybuf.c_cc[VERASE] = 0; ttybuf.c_cc[VKILL] = 0; if ((saveint = safe_signal(SIGINT, SIG_IGN)) == SIG_DFL) safe_signal(SIGINT, SIG_DFL); if ((savequit = safe_signal(SIGQUIT, SIG_IGN)) == SIG_DFL) safe_signal(SIGQUIT, SIG_DFL); #else if (sigsetjmp(intjmp, 1)) { /* avoid garbled output with C-c */ printf("\n"); fflush(stdout); goto out2; } saveint = safe_signal(SIGINT, ttyint); #endif TTYSET_CHECK(string) ret = rtty_internal(prefix, string); if (ret != NULL && *ret == '\0') ret = NULL; out2: safe_signal(SIGTSTP, savetstp); safe_signal(SIGTTOU, savettou); safe_signal(SIGTTIN, savettin); #ifndef TIOCSTI ttybuf.c_cc[VERASE] = c_erase; ttybuf.c_cc[VKILL] = c_kill; if (ttyset) tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); safe_signal(SIGQUIT, savequit); #endif safe_signal(SIGINT, saveint); return ret; } int yorn(char *msg) { char *cp; if (value("interactive") == NULL) return 1; do cp = readtty(msg, NULL); while (cp == NULL || *cp != 'y' && *cp != 'Y' && *cp != 'n' && *cp != 'N'); return *cp == 'y' || *cp == 'Y'; } heirloom-mailx-12.5/v7.local.c000066400000000000000000000062611155563371200161750ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)v7.local.c 2.10 (gritter) 3/4/06"; #endif #endif /* not lint */ /* * Mail -- a mail program * * Version 7 * * Local routines that are installation dependent. */ #include "rcv.h" #include "extern.h" #include #include #include /* * Locate the user's mailbox file (ie, the place where new, unread * mail is queued). */ void findmail(char *user, int force, char *buf, int size) { char *mbox, *cp; if (strcmp(user, myname) == 0 && !force && (cp = value("folder")) != NULL && which_protocol(cp) == PROTO_IMAP) { snprintf(buf, size, "%s/INBOX", protbase(cp)); } else if (force || (mbox = value("MAIL")) == NULL) { snprintf(buf, size, "%s/%s", MAILSPOOL, user); } else { strncpy(buf, mbox, size); buf[size-1]='\0'; } } /* * Get rid of the queued mail. */ void demail(void) { if (value("keep") != NULL || rm(mailname) < 0) close(creat(mailname, 0600)); } /* * Discover user login name. */ char * username(void) { char *np; uid_t uid; if ((np = getenv("USER")) != NULL) return np; if ((np = getname(uid = getuid())) != NULL) return np; printf(catgets(catd, CATSET, 201, "Cannot associate a name with uid %d\n"), (int)uid); return NULL; } heirloom-mailx-12.5/vars.c000066400000000000000000000145511155563371200155240ustar00rootroot00000000000000/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)vars.c 2.12 (gritter) 10/1/08"; #endif #endif /* not lint */ #include "rcv.h" #include "extern.h" /* * Mail -- a mail program * * Variable handling stuff. */ static char *canonify(const char *vn); static void vfree(char *cp); static struct var *lookup(const char *name); static void remove_grouplist(struct grouphead *gh); /* * If a variable name begins with a lowercase-character and contains at * least one '@', it is converted to all-lowercase. This is necessary * for lookups of names based on email addresses. * * Following the standard, only the part following the last '@' should * be lower-cased, but practice has established otherwise here. */ static char * canonify(const char *vn) { const char *vp; if (upperchar(*vn&0377)) return (char *)vn; for (vp = vn; *vp && *vp != '@'; vp++); if (*vp == '@') return i_strdup(vn); return (char *)vn; } /* * Assign a value to a variable. */ void assign(const char *name, const char *value) { struct var *vp; int h; name = canonify(name); h = hash(name); vp = lookup(name); if (vp == NULL) { vp = (struct var *)scalloc(1, sizeof *vp); vp->v_name = vcopy(name); vp->v_link = variables[h]; variables[h] = vp; } else vfree(vp->v_value); vp->v_value = vcopy(value); } /* * Free up a variable string. We do not bother to allocate * strings whose value is "" since they are expected to be frequent. * Thus, we cannot free same! */ static void vfree(char *cp) { if (*cp) free(cp); } /* * Copy a variable value into permanent (ie, not collected after each * command) space. Do not bother to alloc space for "" */ char * vcopy(const char *str) { char *new; unsigned len; if (*str == '\0') return ""; len = strlen(str) + 1; new = smalloc(len); memcpy(new, str, (int) len); return new; } /* * Get the value of a variable and return it. * Look in the environment if its not available locally. */ char * value(const char *name) { struct var *vp; char *vs; name = canonify(name); if ((vp = lookup(name)) == NULL) { if ((vs = getenv(name)) != NULL && *vs) vs = savestr(vs); return vs; } return vp->v_value; } /* * Locate a variable and return its variable * node. */ static struct var * lookup(const char *name) { struct var *vp; for (vp = variables[hash(name)]; vp != NULL; vp = vp->v_link) if (*vp->v_name == *name && equal(vp->v_name, name)) return(vp); return(NULL); } /* * Locate a group name and return it. */ struct grouphead * findgroup(char *name) { struct grouphead *gh; for (gh = groups[hash(name)]; gh != NULL; gh = gh->g_link) if (*gh->g_name == *name && equal(gh->g_name, name)) return(gh); return(NULL); } /* * Print a group out on stdout */ void printgroup(char *name) { struct grouphead *gh; struct group *gp; if ((gh = findgroup(name)) == NULL) { printf(catgets(catd, CATSET, 202, "\"%s\": not a group\n"), name); return; } printf("%s\t", gh->g_name); for (gp = gh->g_list; gp != NULL; gp = gp->ge_link) printf(" %s", gp->ge_name); putchar('\n'); } /* * Hash the passed string and return an index into * the variable or group hash table. */ int hash(const char *name) { int h = 0; while (*name) { h <<= 2; h += *name++; } if (h < 0 && (h = -h) < 0) h = 0; return (h % HSHSIZE); } int unset_internal(const char *name) { struct var *vp, *vp2; int h; name = canonify(name); if ((vp2 = lookup(name)) == NULL) { if (!sourcing && !unset_allow_undefined) { printf(catgets(catd, CATSET, 203, "\"%s\": undefined variable\n"), name); return 1; } return 0; } h = hash(name); if (vp2 == variables[h]) { variables[h] = variables[h]->v_link; vfree(vp2->v_name); vfree(vp2->v_value); free(vp2); return 0; } for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link); vp->v_link = vp2->v_link; vfree(vp2->v_name); vfree(vp2->v_value); free(vp2); return 0; } static void remove_grouplist(struct grouphead *gh) { struct group *gp, *gq; if ((gp = gh->g_list) != NULL) { for (; gp; gp = gq) { gq = gp->ge_link; vfree(gp->ge_name); free(gp); } } } void remove_group(const char *name) { struct grouphead *gh, *gp = NULL; int h = hash(name); for (gh = groups[h]; gh != NULL; gh = gh->g_link) { if (*gh->g_name == *name && equal(gh->g_name, name)) { remove_grouplist(gh); vfree(gh->g_name); if (gp != NULL) gp->g_link = gh->g_link; else groups[h] = NULL; free(gh); break; } gp = gh; } } heirloom-mailx-12.5/version.c000066400000000000000000000131551155563371200162350ustar00rootroot00000000000000#define V "12.5" /* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)version.c 2.403 (gritter) 6/20/10"; #endif #endif /* not lint */ /* * Just keep track of the date/sid of this version of Mail. * Load this file first to get a "total" Mail version. */ /*char *version = "8.1 6/6/93";*/ const char *version = V " 6/20/10"; #ifndef lint #if __GNUC__ >= 3 && __GNUC_MINOR__ >= 4 || __GNUC__ >= 4 #define USED __attribute__ ((used)) #elif defined __GNUC__ #define USED __attribute__ ((unused)) #else #define USED #endif static const char *versionid USED = "@(#)mailx " V " (gritter) 6/20/10"; #endif /* !lint */ /* SLIST */ /* aux.c:static char sccsid[] = "@(#)aux.c 2.83 (gritter) 3/4/06"; base64.c:static char sccsid[] = "@(#)base64.c 2.14 (gritter) 4/21/06"; cache.c:static char sccsid[] = "@(#)cache.c 1.61 (gritter) 3/4/06"; cmd1.c:static char sccsid[] = "@(#)cmd1.c 2.97 (gritter) 6/16/07"; cmd2.c:static char sccsid[] = "@(#)cmd2.c 2.47 (gritter) 5/9/10"; cmd3.c:static char sccsid[] = "@(#)cmd3.c 2.87 (gritter) 10/1/08"; cmdtab.c:static char sccsid[] = "@(#)cmdtab.c 2.51 (gritter) 3/4/06"; collect.c:static char sccsid[] = "@(#)collect.c 2.54 (gritter) 6/16/07"; def.h: * Sccsid @(#)def.h 2.104 (gritter) 3/4/06 dotlock.c:static char sccsid[] = "@(#)dotlock.c 2.9 (gritter) 3/20/06"; edit.c:static char sccsid[] = "@(#)edit.c 2.24 (gritter) 3/4/06"; extern.h: * Sccsid @(#)extern.h 2.162 (gritter) 10/1/08 fio.c:static char sccsid[] = "@(#)fio.c 2.76 (gritter) 9/16/09"; getname.c:static char sccsid[] = "@(#)getname.c 2.5 (gritter) 3/4/06"; getopt.c: Sccsid @(#)getopt.c 1.7 (gritter) 12/16/07 glob.h: * Sccsid @(#)glob.h 2.27 (gritter) 6/16/07 head.c:static char sccsid[] = "@(#)head.c 2.17 (gritter) 3/4/06"; hmac.c: Sccsid @(#)hmac.c 1.8 (gritter) 3/4/06 imap.c:static char sccsid[] = "@(#)imap.c 1.222 (gritter) 3/13/09"; imap_gssapi.c:static char sccsid[] = "@(#)imap_gssapi.c 1.10 (gritter) 3/4/06"; imap_search.c:static char sccsid[] = "@(#)imap_search.c 1.29 (gritter) 3/4/06"; junk.c:static char sccsid[] = "@(#)junk.c 1.75 (gritter) 9/14/08"; lex.c:static char sccsid[] = "@(#)lex.c 2.86 (gritter) 12/25/06"; list.c:static char sccsid[] = "@(#)list.c 2.62 (gritter) 12/11/08"; lzw.c: * Sccsid @(#)lzw.c 1.11 (gritter) 3/4/06 macro.c:static char sccsid[] = "@(#)macro.c 1.13 (gritter) 3/4/06"; maildir.c:static char sccsid[] = "@(#)maildir.c 1.20 (gritter) 12/28/06"; main.c:static char sccsid[] = "@(#)main.c 2.51 (gritter) 10/1/07"; md5.c: Sccsid @(#)md5.c 1.8 (gritter) 3/4/06 md5.h: Sccsid @(#)md5.h 1.8 (gritter) 3/4/06 mime.c:static char sccsid[] = "@(#)mime.c 2.70 (gritter) 3/10/09"; names.c:static char sccsid[] = "@(#)names.c 2.22 (gritter) 3/4/06"; nss.c:static char sccsid[] = "@(#)nss.c 1.48 (gritter) 8/4/07"; openssl.c:static char sccsid[] = "@(#)openssl.c 1.26 (gritter) 5/26/09"; pop3.c:static char sccsid[] = "@(#)pop3.c 2.43 (gritter) 3/4/06"; popen.c:static char sccsid[] = "@(#)popen.c 2.20 (gritter) 3/4/06"; quit.c:static char sccsid[] = "@(#)quit.c 2.30 (gritter) 11/11/08"; rcv.h: * Sccsid @(#)rcv.h 2.7 (gritter) 3/4/06 send.c:static char sccsid[] = "@(#)send.c 2.86 (gritter) 2/4/08"; sendout.c:static char sccsid[] = "@(#)sendout.c 2.100 (gritter) 3/1/09"; smtp.c:static char sccsid[] = "@(#)smtp.c 2.43 (gritter) 8/4/07"; ssl.c:static char sccsid[] = "@(#)ssl.c 1.39 (gritter) 6/12/06"; strings.c:static char sccsid[] = "@(#)strings.c 2.6 (gritter) 3/4/06"; temp.c:static char sccsid[] = "@(#)temp.c 2.8 (gritter) 3/4/06"; thread.c:static char sccsid[] = "@(#)thread.c 1.57 (gritter) 3/4/06"; tty.c:static char sccsid[] = "@(#)tty.c 2.29 (gritter) 3/9/07"; v7.local.c:static char sccsid[] = "@(#)v7.local.c 2.10 (gritter) 3/4/06"; vars.c:static char sccsid[] = "@(#)vars.c 2.12 (gritter) 10/1/08"; */